# ******************************************************************************
#  Copyright (c) 2021-2022. Kneron Inc. All rights reserved.                   *
# ******************************************************************************

from .KPBaseClass.ValueBase import ValueRepresentBase, ValueBase
from .KPWrapperUtils import KPWrapperUtils as wrapper_utils
from .KPStructure import \
    DeviceDescriptorBuffer, \
    DeviceDescriptorListBuffer, \
    DdrManageAttributesBuffer, \
    DeviceGroupBuffer, \
    DeviceGroupPointerBuffer, \
    BaseVersionBuffer, \
    QuantizedFixedPointDescriptorBuffer, \
    QuantizationParametersBuffer, \
    TensorDescriptorBuffer, \
    SingleModelDescriptorBuffer, \
    ModelNefMetadataBuffer, \
    ModelNefDescriptorBuffer, \
    FirmwareVersionBuffer, \
    SystemInfoBuffer, \
    InfConfigurationBuffer, \
    InfCropBoxBuffer, \
    HwPreProcInfoBuffer, \
    GenericRawResultNDArrayBuffer, \
    GenericInputNodeImageBuffer, \
    GenericImageInferenceDescriptorBuffer, \
    GenericImageInferenceResultHeaderBuffer, \
    GenericInputNodeDataBuffer, \
    GenericDataInferenceImageHeaderBuffer, \
    GenericDataInferenceResultHeaderBuffer, \
    InfFixedNodeOutputBuffer, \
    InfFloatNodeOutputBuffer, \
    ProfileModelStatisticsBuffer, \
    ProfileDataBuffer, \
    NpuPerformanceMonitorStatisticsBuffer, \
    PerformanceMonitorDataBuffer, \
    BoundingBoxBuffer, \
    PointBuffer, \
    LandMarkBuffer, \
    ClassificationBuffer, \
    FaceRecognizeBuffer
from .KPEnum import *
from .KPConstant import Const
from .KPException import _check_api_return_code
from typing import Union, List
import numpy as np
import ctypes


class DeviceDescriptor(ValueBase, ValueRepresentBase):
    """
    Information of one connected device from USB perspectives.

    Attributes
    ----------
    usb_port_id : int, default=0
        An unique ID representing for a Kneron device, can be used as input while connecting devices.
    vendor_id : int, default=0
        Supposed to be 0x3231.
    product_id : int, default=0
        USB PID (Product ID).
    link_speed : UsbSpeed, default=UsbSpeed.KP_USB_SPEED_UNKNOWN
        Enum for USB speed mode.
    kn_number : int, default=0
        KN number.
    is_connectable : bool, default=False
        Indicate if this device is connectable.
    usb_port_path : str, default=''
        "busNo-hub_portNo-device_portNo" (ex: "1-2-3", means bus 1 - (hub) port 2 - (device) port 3)
    firmware : str, default=''
        Firmware description.
    """

    def __init__(self,
                 usb_port_id: int = 0,
                 vendor_id: int = 0,
                 product_id: int = 0,
                 link_speed: UsbSpeed = UsbSpeed.KP_USB_SPEED_UNKNOWN,
                 kn_number: int = 0,
                 is_connectable: bool = False,
                 usb_port_path: str = '',
                 firmware: str = ''):
        """
        Information of one connected device from USB perspectives.

        Parameters
        ----------
        usb_port_id : int, default=0
            An unique ID representing for a Kneron device, can be used as input while connecting devices.
        vendor_id : int, default=0
            Supposed to be 0x3231.
        product_id : int, default=0
            USB PID (Product ID).
        link_speed : UsbSpeed, default=UsbSpeed.KP_USB_SPEED_UNKNOWN
            Enum for USB speed mode.
        kn_number : int, default=0
            KN number.
        is_connectable : bool, default=False
            Indicate if this device is connectable.
        usb_port_path : str, default=''
            "busNo-hub_portNo-device_portNo" (ex: "1-2-3", means bus 1 - (hub) port 2 - (device) port 3)
        firmware : str, default=''
            Firmware description.
        """
        ValueBase.__init__(self,
                           element_buffer_class=DeviceDescriptorBuffer,
                           usb_port_id=usb_port_id,
                           vendor_id=vendor_id,
                           product_id=product_id,
                           link_speed=link_speed,
                           kn_number=kn_number,
                           is_connectable=is_connectable,
                           usb_port_path=usb_port_path,
                           firmware=firmware)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def usb_port_id(self) -> int:
        """
        int: An unique ID representing for a Kneron device, can be used as input while connecting devices.
        """
        return self._element_buffer._port_id

    @property
    def vendor_id(self) -> int:
        """
        int: Supposed to be 0x3231.
        """
        return self._element_buffer._vendor_id

    @property
    def product_id(self) -> int:
        """
        int: USB PID (Product ID).
        """
        return self._element_buffer._product_id

    @property
    def link_speed(self) -> UsbSpeed:
        """
        UsbSpeed: Enum for USB speed mode.
        """
        return UsbSpeed(self._element_buffer._link_speed)

    @property
    def kn_number(self) -> int:
        """
        int: KN number.
        """
        return self._element_buffer._kn_number

    @property
    def is_connectable(self) -> bool:
        """
        bool: Indicate if this device is connectable.
        """
        return self._element_buffer._isConnectable

    @property
    def usb_port_path(self) -> str:
        """
        str: "busNo-hub_portNo-device_portNo" (ex: "1-2-3", means bus 1 - (hub) port 2 - (device) port 3)
        """
        return self._element_buffer._port_path.decode('utf-8')

    @property
    def firmware(self) -> str:
        """
        str: Firmware description.
        """
        return self._element_buffer._firmware.decode('utf-8')

    def get_member_variable_dict(self) -> dict:
        return {
            'usb_port_id': self.usb_port_id,
            'vendor_id': '0x{:X}'.format(self.vendor_id),
            'product_id': '0x{:X}'.format(self.product_id),
            'link_speed': str(self.link_speed),
            'kn_number': '0x{:X}'.format(self.kn_number),
            'is_connectable': self.is_connectable,
            'usb_port_path': self.usb_port_path,
            'firmware': self.firmware
        }


class DeviceDescriptorList(ValueBase, ValueRepresentBase):
    """
    Information of connected devices from USB perspectives.

    Attributes
    ----------
    device_descriptor_list : List[kp.DeviceDescriptor], default=[]
        DeviceDescriptor objects list, contain information of connected devices from USB perspectives.


    See Also
    --------
    kp.core.scan_devices : Scan all Kneron devices and report a list.
    kp.DeviceDescriptor
    """

    def __init__(self, device_descriptor_list: List[DeviceDescriptor] = []):
        """
        Information of connected devices from USB perspectives.

        Parameters
        ----------
        device_descriptor_list : List[kp.DeviceDescriptor], default=[]
            DeviceDescriptor objects list, contain information of connected devices from USB perspectives.
        """
        ValueBase.__init__(self,
                           element_buffer_class=DeviceDescriptorListBuffer,
                           device_descriptor_buffer_list=DeviceDescriptorList.__cast_list_to_buffer_list(
                               device_descriptor_list=device_descriptor_list))

    @staticmethod
    def __cast_list_to_buffer_list(device_descriptor_list: List[DeviceDescriptor]) -> List[DeviceDescriptorBuffer]:
        return [DeviceDescriptorBuffer(usb_port_id=device_descriptor.usb_port_id,
                                       vendor_id=device_descriptor.vendor_id,
                                       product_id=device_descriptor.product_id,
                                       link_speed=device_descriptor.link_speed,
                                       kn_number=device_descriptor.kn_number,
                                       is_connectable=device_descriptor.is_connectable,
                                       usb_port_path=device_descriptor.usb_port_path,
                                       firmware=device_descriptor.firmware) for device_descriptor in
                device_descriptor_list]

    def _cast_element_buffer(self) -> None:
        self._element_buffer = ctypes.pointer(self._element_buffer)

    def _get_element_buffer(self) -> Union[None, ctypes.POINTER]:
        return self._element_buffer

    @property
    def device_descriptor_number(self) -> int:
        """
        int: Number of connected devices.
        """
        return self._element_buffer.contents._num_dev

    @property
    def device_descriptor_list(self) -> List[DeviceDescriptor]:
        """
        List[kp.DeviceDescriptor]: DeviceDescriptor objects list, contain information of connected devices from USB
        perspectives.
        """
        device_descriptor_list = []

        if 0 < self._element_buffer.contents._num_dev:
            if self._is_allocate_from_c:
                _address = ctypes.addressof(self._element_buffer.contents._device)
                _device_descriptor_buffer_list = list(
                    (DeviceDescriptorBuffer * self._element_buffer.contents._num_dev).from_address(_address))[
                                                 :self._element_buffer.contents._num_dev]
            else:
                _device_descriptor_buffer_list = self._element_buffer.contents._device[
                                                 :self._element_buffer.contents._num_dev]

            device_descriptor_list = [DeviceDescriptor(
                usb_port_id=device_descriptor_buffer._port_id,
                vendor_id=device_descriptor_buffer._vendor_id,
                product_id=device_descriptor_buffer._product_id,
                link_speed=UsbSpeed(device_descriptor_buffer._link_speed),
                kn_number=device_descriptor_buffer._kn_number,
                is_connectable=device_descriptor_buffer._isConnectable,
                usb_port_path=device_descriptor_buffer._port_path.decode('utf-8'),
                firmware=device_descriptor_buffer._firmware.decode('utf-8'),
            ) for device_descriptor_buffer in _device_descriptor_buffer_list]

        return device_descriptor_list

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {}

        for idx, device_descriptor in enumerate(self.device_descriptor_list):
            member_variable_dict[idx] = device_descriptor.get_member_variable_dict()

        return member_variable_dict


class BaseVersion(ValueBase, ValueRepresentBase):
    """
    A basic version object.

    Attributes
    ----------
    major : int, default=0
        Major number.
    minor : int, default=0
        Minor number.
    revision : int, default=0
        Revision number.
    """

    def __init__(self,
                 major: int = 0,
                 minor: int = 0,
                 revision: int = 0):
        """
        A basic version object.

        Parameters
        ----------
        major : int, default=0
            Major number.
        minor : int, default=0
            Minor number.
        revision : int, default=0
            Revision number.
        """
        ValueBase.__init__(self,
                           element_buffer_class=BaseVersionBuffer,
                           major=major,
                           minor=minor,
                           revision=revision)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def major(self) -> int:
        """
        int: Major number.
        """
        return self._element_buffer._major

    @property
    def minor(self) -> int:
        """
        int: Minor number.
        """
        return self._element_buffer._minor

    @property
    def revision(self) -> int:
        """
        int: Revision number.
        """
        return self._element_buffer._revision

    def get_member_variable_dict(self) -> dict:
        return {
            'version': '{}.{}.{}'.format(self.major,
                                         self.minor,
                                         self.revision)
        }


class NefSchemaVersion(BaseVersion):
    """
    A NEF schema version object.
    """


class SetupSchemaVersion(BaseVersion):
    """
    A setup information schema version object.
    """


class SetupFileSchemaVersion(BaseVersion):
    """
    A setup information file version object.
    """


class QuantizedFixedPointDescriptor(ValueBase, ValueRepresentBase):
    """
    Quantization parameters for fixed-point value.

    Attributes
    ----------
    scale : float, default=0
        Scale for fixed/floating point conversion.
    radix : int, default=0
        Radix for fixed/floating point conversion.
    """

    def __init__(self,
                 scale: float = 0,
                 radix: int = 0):
        """
        Quantization parameters for fixed-point tensor.

        Parameters
        ----------
        scale : float, default=0
            Scale for fixed/floating point conversion.
        radix : int, default=0
            Radix for fixed/floating point conversion.
        """
        ValueBase.__init__(self,
                           element_buffer_class=QuantizedFixedPointDescriptorBuffer,
                           scale=scale,
                           radix=radix)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def scale(self) -> float:
        """
        float: Scale for fixed/floating point conversion.
        """
        return self._element_buffer._scale

    @property
    def radix(self) -> int:
        """
        int: Radix for fixed/floating point conversion.
        """
        return self._element_buffer._radix

    def get_member_variable_dict(self) -> dict:
        return {
            'scale': self.scale,
            'radix': self.radix,
        }


class QuantizationParameters(ValueBase, ValueRepresentBase):
    """
    Quantization parameters for tensor.

    Attributes
    ----------
    quantized_fixed_point_descriptor_list : List[QuantizedFixedPointDescriptor], default=[]
        (a) List length = 1 for all-channel fixed-point quantization parameter, (b) List length > 1 for per-channel fixed-point quantization parameter.
    """

    def __init__(self,
                 quantized_fixed_point_descriptor_list: List[QuantizedFixedPointDescriptor] = []):
        """
        Quantization parameters for tensor.

        Parameters
        ----------
        quantized_fixed_point_descriptor_list : List[QuantizedFixedPointDescriptor], default=[]
            (a) List length = 1 for all-channel fixed-point quantization parameter, (b) List length > 1 for per-channel fixed-point quantization parameter.
        """
        ValueBase.__init__(self,
                           element_buffer_class=QuantizationParametersBuffer,
                           quantized_fixed_point_descriptor_num=len(quantized_fixed_point_descriptor_list),
                           quantized_fixed_point_descriptor_list=[
                               QuantizedFixedPointDescriptorBuffer(scale=quantized_fixed_point_descriptor.scale,
                                                                   radix=quantized_fixed_point_descriptor.radix) for
                               quantized_fixed_point_descriptor in
                               quantized_fixed_point_descriptor_list])

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def quantized_fixed_point_descriptor_list(self) -> List[QuantizedFixedPointDescriptor]:
        """
        List[QuantizedFixedPointDescriptor]: (a) List length = 1 for all-channel fixed-point quantization parameter, (b) List length > 1 for per-channel fixed-point quantization parameter.
        """
        return [QuantizedFixedPointDescriptor(scale=quantized_fixed_point_descriptor_buffer._scale,
                                              radix=quantized_fixed_point_descriptor_buffer._radix) for
                quantized_fixed_point_descriptor_buffer in self._element_buffer._quantized_fixed_point_descriptor[
                                                           : self._element_buffer._quantized_fixed_point_descriptor_num]]

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'quantized_fixed_point_descriptor_list': {}
        }

        for idx, quantized_fixed_point_descriptor in enumerate(self.quantized_fixed_point_descriptor_list):
            member_variable_dict['quantized_fixed_point_descriptor_list'][
                idx] = quantized_fixed_point_descriptor.get_member_variable_dict()

        return member_variable_dict


class TensorDescriptor(ValueBase, ValueRepresentBase):
    """
    Tensor information.

    Attributes
    ----------
    index : int, default=0
        Index number of the tensor.
    name : str, default=''
        Name of the tensor.
    shape_npu : List[int], default=[]
        NPU shape of the tensor.
    shape_onnx : List[int], default=[]
        ONNX shape of the tensor.
    data_layout : ModelTensorDataLayout, default=ModelTensorDataLayout.KP_MODEL_TENSOR_DATA_LAYOUT_UNKNOWN
        NPU data layout of the tensor.
    quantization_parameters : QuantizationParameters, default=QuantizationParameters()
        Quantization parameters f the tensor.
    """

    def __init__(self,
                 index: int = 0,
                 name: str = '',
                 shape_npu: List[int] = [],
                 shape_onnx: List[int] = [],
                 data_layout: ModelTensorDataLayout = ModelTensorDataLayout.KP_MODEL_TENSOR_DATA_LAYOUT_UNKNOWN,
                 quantization_parameters: QuantizationParameters = QuantizationParameters()):
        """
        Quantization parameters for tensor.

        Parameters
        ----------
        index : int, default=0
            Index number of the tensor.
        name : str, default=''
            Name of the tensor.
        shape_npu : List[int], default=[]
            NPU shape of the tensor.
        shape_onnx : List[int], default=[]
            ONNX shape of the tensor.
        data_layout : ModelTensorDataLayout, default=ModelTensorDataLayout.KP_MODEL_TENSOR_DATA_LAYOUT_UNKNOWN
            NPU data layout of the tensor.
        quantization_parameters : QuantizationParameters, default=QuantizationParameters()
            Quantization parameters f the tensor.
        """
        ValueBase.__init__(self,
                           element_buffer_class=TensorDescriptorBuffer,
                           index=index,
                           name=name,
                           shape_npu_len=len(shape_npu),
                           shape_npu=shape_npu,
                           shape_onnx_len=len(shape_onnx),
                           shape_onnx=shape_onnx,
                           data_layout=data_layout.value,
                           quantization_parameters=QuantizationParametersBuffer(
                               quantized_fixed_point_descriptor_num=len(
                                   quantization_parameters.quantized_fixed_point_descriptor_list),
                               quantized_fixed_point_descriptor_list=[
                                   QuantizedFixedPointDescriptorBuffer(
                                       scale=quantized_fixed_point_descriptor.scale,
                                       radix=quantized_fixed_point_descriptor.radix
                                   ) for quantized_fixed_point_descriptor in
                                   quantization_parameters.quantized_fixed_point_descriptor_list
                               ]
                           ))

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def index(self) -> int:
        """
        int: Index number of the tensor.
        """
        return self._element_buffer._index

    @property
    def name(self) -> str:
        """
        str: Name of the tensor.
        """
        return self._element_buffer._name.decode("utf-8")

    @property
    def shape_npu(self) -> List[int]:
        """
        List[int]: NPU shape of the tensor.
        """
        return [shape_element for shape_element in
                self._element_buffer._shape_npu[:self._element_buffer._shape_npu_len]]

    @property
    def shape_onnx(self) -> List[int]:
        """
        List[int]: ONNX shape of the tensor.
        """
        return [shape_element for shape_element in
                self._element_buffer._shape_onnx[:self._element_buffer._shape_onnx_len]]

    @property
    def data_layout(self) -> ModelTensorDataLayout:
        """
        ModelTensorDataLayout: NPU data layout of the tensor.
        """
        return ModelTensorDataLayout(self._element_buffer._data_layout)

    @property
    def quantization_parameters(self) -> QuantizationParameters:
        """
        QuantizationParameters: Quantization parameters f the tensor.
        """
        quantization_parameters_buffer = self._element_buffer._quantization_parameters

        return QuantizationParameters(
            quantized_fixed_point_descriptor_list=[
                QuantizedFixedPointDescriptor(
                    scale=quantized_fixed_point_descriptor_buffer._scale,
                    radix=quantized_fixed_point_descriptor_buffer._radix
                ) for quantized_fixed_point_descriptor_buffer in
                quantization_parameters_buffer._quantized_fixed_point_descriptor[
                :quantization_parameters_buffer._quantized_fixed_point_descriptor_num]
            ]
        )

    def get_member_variable_dict(self) -> dict:
        return {
            'index': self.index,
            'name': self.name,
            'shape_npu': self.shape_npu,
            'shape_onnx': self.shape_onnx,
            'data_layout': str(self.data_layout),
            'quantization_parameters': self.quantization_parameters.get_member_variable_dict()
        }


class SingleModelDescriptor(ValueBase, ValueRepresentBase):
    """
    A basic descriptor for a model.

    Attributes
    ----------
    target_chip : ModelTargetChip, default=KP_MODEL_TARGET_CHIP_UNKNOWN
        Target chip of model.
    version : int, default=0
        Version of model.
    id : int, default=0
        Model ID.
    input_nodes : List[TensorDescriptor], default=[]
        List of model input node tensor information.
    output_nodes : List[TensorDescriptor], default=[]
        List of model output node tensor information.
    setup_schema_version : SetupSchemaVersion, default=SetupSchemaVersion()
        Schema version of setup.
    setup_file_schema_version : SetupFileSchemaVersion, default=SetupFileSchemaVersion()
        File schema version of setup.
    max_raw_out_size : int, default=0
        Needed raw output buffer size for this model.
    """

    def __init__(self,
                 target_chip: ModelTargetChip = ModelTargetChip.KP_MODEL_TARGET_CHIP_UNKNOWN,
                 version: int = 0,
                 id: int = 0,
                 input_nodes: List[TensorDescriptor] = [],
                 output_nodes: List[TensorDescriptor] = [],
                 setup_schema_version: SetupSchemaVersion = SetupSchemaVersion(),
                 setup_file_schema_version: SetupFileSchemaVersion = SetupFileSchemaVersion(),
                 max_raw_out_size: int = 0):
        """
        Quantization parameters for tensor.

        Parameters
        ----------
        target_chip : ModelTargetChip, default=KP_MODEL_TARGET_CHIP_UNKNOWN
            Target chip of model.
        version : int, default=0
            Version of model.
        id : int, default=0
            Model ID.
        input_nodes : List[TensorDescriptor], default=[]
            List of model input node tensor information.
        output_nodes : List[TensorDescriptor], default=[]
            List of model output node tensor information.
        setup_schema_version : SetupSchemaVersion, default=SetupSchemaVersion()
            Schema version of setup.
        setup_file_schema_version : SetupFileSchemaVersion, default=SetupFileSchemaVersion()
            File schema version of setup.
        max_raw_out_size : int, default=0
            Needed raw output buffer size for this model.
        """
        ValueBase.__init__(self,
                           element_buffer_class=SingleModelDescriptorBuffer,
                           target=target_chip.value,
                           version=version,
                           id=id,
                           input_nodes_num=len(input_nodes),
                           input_nodes=SingleModelDescriptor._construct_tensor_descriptor_buffer_list(
                               tensor_descriptor_list=input_nodes),
                           output_nodes_num=len(output_nodes),
                           output_nodes=SingleModelDescriptor._construct_tensor_descriptor_buffer_list(
                               tensor_descriptor_list=output_nodes),
                           setup_bin_schema_version=BaseVersionBuffer(major=setup_schema_version.major,
                                                                      minor=setup_schema_version.minor,
                                                                      revision=setup_schema_version.revision),
                           file_schema_version=BaseVersionBuffer(major=setup_file_schema_version.major,
                                                                 minor=setup_file_schema_version.minor,
                                                                 revision=setup_file_schema_version.revision),
                           max_raw_out_size=max_raw_out_size)

    def _cast_element_buffer(self) -> None:
        pass

    @staticmethod
    def _construct_tensor_descriptor_list(tensor_descriptor_buffer_list: List[TensorDescriptorBuffer]) -> List[
        TensorDescriptor]:
        return [
            TensorDescriptor(
                index=tensor_descriptor_buffer._index,
                name=tensor_descriptor_buffer._name.decode('utf-8'),
                shape_npu=[dim_value for dim_value in
                           tensor_descriptor_buffer._shape_npu[:tensor_descriptor_buffer._shape_npu_len]],
                shape_onnx=[dim_value for dim_value in
                            tensor_descriptor_buffer._shape_onnx[:tensor_descriptor_buffer._shape_onnx_len]],
                data_layout=ModelTensorDataLayout(tensor_descriptor_buffer._data_layout),
                quantization_parameters=QuantizationParameters(
                    quantized_fixed_point_descriptor_list=[
                        QuantizedFixedPointDescriptor(
                            scale=quantized_fixed_point_descriptor_buffer._scale,
                            radix=quantized_fixed_point_descriptor_buffer._radix
                        ) for quantized_fixed_point_descriptor_buffer in
                        tensor_descriptor_buffer._quantization_parameters._quantized_fixed_point_descriptor[
                        :tensor_descriptor_buffer._quantization_parameters._quantized_fixed_point_descriptor_num]
                    ]
                )
            ) for tensor_descriptor_buffer in tensor_descriptor_buffer_list
        ]

    @staticmethod
    def _construct_tensor_descriptor_buffer_list(tensor_descriptor_list: List[TensorDescriptor]) -> List[
        TensorDescriptorBuffer]:
        return [
            TensorDescriptorBuffer(
                index=tensor_descriptor.index,
                name=tensor_descriptor.name,
                shape_npu_len=len(tensor_descriptor.shape_npu),
                shape_npu=tensor_descriptor.shape_npu,
                shape_onnx_len=len(tensor_descriptor.shape_onnx),
                shape_onnx=tensor_descriptor.shape_onnx,
                data_layout=tensor_descriptor.data_layout.value,
                quantization_parameters=QuantizationParametersBuffer(
                    quantized_fixed_point_descriptor_num=len(
                        tensor_descriptor.quantization_parameters.quantized_fixed_point_descriptor_list),
                    quantized_fixed_point_descriptor_list=[
                        QuantizedFixedPointDescriptorBuffer(
                            scale=quantized_fixed_point_descriptor.scale,
                            radix=quantized_fixed_point_descriptor.radix
                        ) for quantized_fixed_point_descriptor in
                        tensor_descriptor.quantization_parameters.quantized_fixed_point_descriptor_list
                    ]
                )
            ) for tensor_descriptor in tensor_descriptor_list
        ]

    @property
    def target_chip(self) -> ModelTargetChip:
        """
        ModelTargetChip: Target chip of model.
        """
        return ModelTargetChip(self._element_buffer._target)

    @property
    def version(self) -> int:
        """
        int: Version of model.
        """
        return self._element_buffer._version

    @property
    def id(self) -> int:
        """
        int: Model ID.
        """
        return self._element_buffer._id

    @property
    def input_nodes(self) -> List[TensorDescriptor]:
        """
        List[TensorDescriptor]: List of model input node tensor information.
        """
        return SingleModelDescriptor._construct_tensor_descriptor_list(
            tensor_descriptor_buffer_list=self._element_buffer._input_nodes[:self._element_buffer._input_nodes_num]
        )

    @property
    def output_nodes(self) -> List[TensorDescriptor]:
        """
        List[TensorDescriptor]: List of model output node tensor information.
        """
        return SingleModelDescriptor._construct_tensor_descriptor_list(
            tensor_descriptor_buffer_list=self._element_buffer._output_nodes[:self._element_buffer._output_nodes_num]
        )

    @property
    def setup_schema_version(self) -> SetupSchemaVersion:
        """
        SetupSchemaVersion: Schema version of setup.
        """
        return SetupSchemaVersion(
            major=self._element_buffer._setup_bin_schema_version._major,
            minor=self._element_buffer._setup_bin_schema_version._minor,
            revision=self._element_buffer._setup_bin_schema_version._revision
        )

    @property
    def setup_file_schema_version(self) -> SetupFileSchemaVersion:
        """
        SetupFileSchemaVersion: File schema version of setup.
        """
        return SetupFileSchemaVersion(
            major=self._element_buffer._file_schema_version._major,
            minor=self._element_buffer._file_schema_version._minor,
            revision=self._element_buffer._file_schema_version._revision
        )

    @property
    def max_raw_out_size(self) -> int:
        """
        int: Needed raw output buffer size for this model.
        """
        return self._element_buffer._max_raw_out_size

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'target_chip': str(self.target_chip),
            'version': '0x{:X}'.format(self.version),
            'id': self.id,
            'input_nodes': {},
            'output_nodes': {},
            'setup_schema_version': self.setup_schema_version.get_member_variable_dict(),
            'setup_file_schema_version': self.setup_file_schema_version.get_member_variable_dict(),
            'max_raw_out_size': self.max_raw_out_size
        }

        for idx, tensor in enumerate(self.input_nodes):
            member_variable_dict['input_nodes'][idx] = tensor.get_member_variable_dict()

        for idx, tensor in enumerate(self.output_nodes):
            member_variable_dict['output_nodes'][idx] = tensor.get_member_variable_dict()

        return member_variable_dict


class ModelNefMetadata(ValueBase, ValueRepresentBase):
    """
    A basic descriptor for a model NEF metadata.

    Attributes
    ----------
    kn_number : int, default=0
        Target KN number device of encrypted all models.
    toolchain_version : str, default=''
        Toolchain version of all models.
    compiler_version : str, default=''
        Compiler version of all models.
    nef_schema_version : NefSchemaVersion, default=NefSchemaVersion()
        Schema version of nef.
    platform : str, default=''
        Target device platform USB dongle, 96 board, etc.
    """

    def __init__(self,
                 kn_number: int = 0,
                 toolchain_version: str = '',
                 compiler_version: str = '',
                 nef_schema_version: NefSchemaVersion = NefSchemaVersion(),
                 platform: str = ''):
        """
        A basic descriptor for a model NEF metadata.

        Parameters
        ----------
        kn_number : int, default=0
            Target KN number device of encrypted all models.
        toolchain_version : str, default=''
            Toolchain version of all models.
        compiler_version : str, default=''
            Compiler version of all models.
        nef_schema_version : NefSchemaVersion, default=NefSchemaVersion()
            Schema version of nef.
        platform : str, default=''
            Target device platform USB dongle, 96 board, etc.
        """
        ValueBase.__init__(self,
                           element_buffer_class=ModelNefMetadataBuffer,
                           kn_num=kn_number,
                           toolchain_version=toolchain_version,
                           compiler_version=compiler_version,
                           nef_schema_version=BaseVersionBuffer(major=nef_schema_version.major,
                                                                minor=nef_schema_version.minor,
                                                                revision=nef_schema_version.revision),
                           platform=platform)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def kn_number(self) -> int:
        """
        int: Target KN number device of encrypted all models.
        """
        return self._element_buffer._kn_num

    @property
    def toolchain_version(self) -> str:
        """
        str: Toolchain version of all models.
        """
        return self._element_buffer._toolchain_version.decode("utf-8")

    @property
    def compiler_version(self) -> str:
        """
        str: Compiler version of all models.
        """
        return self._element_buffer._compiler_version.decode("utf-8")

    @property
    def nef_schema_version(self) -> NefSchemaVersion:
        """
        NefSchemaVersion: Schema version of nef.
        """
        return NefSchemaVersion(
            major=self._element_buffer._nef_schema_version._major,
            minor=self._element_buffer._nef_schema_version._minor,
            revision=self._element_buffer._nef_schema_version._revision
        )

    @property
    def platform(self) -> str:
        """
        str: Target device platform USB dongle, 96 board, etc.
        """
        return self._element_buffer._platform.decode("utf-8")

    def get_member_variable_dict(self) -> dict:
        return {
            'kn_number': '0x{:X}'.format(self.kn_number),
            'toolchain_version': self.toolchain_version,
            'compiler_version': self.compiler_version,
            'nef_schema_version': self.nef_schema_version.get_member_variable_dict(),
            'platform': self.platform
        }


class ModelNefDescriptor(ValueBase, ValueRepresentBase):
    """
    A basic descriptor for NEF.

    Attributes
    ----------
    magic : int, default=0
        Magic number for model_nef_descriptor (0x5AA55AA5).
    metadata : ModelNefMetadata, default=ModelNefMetadata()
        NEF metadata.
    target_chip : ModelTargetChip, default=ModelTargetChip.KP_MODEL_TARGET_CHIP_UNKNOWN
        Target chip of all models.
    crc : int, default=0
        CRC of all models.
    models : List[SingleModelDescriptor], default=[]
        Model descriptors.
    """

    def __init__(self,
                 magic: int = 0,
                 metadata: ModelNefMetadata = ModelNefMetadata(),
                 target_chip: ModelTargetChip = ModelTargetChip.KP_MODEL_TARGET_CHIP_UNKNOWN,
                 crc: int = 0,
                 models: List[SingleModelDescriptor] = []):
        """
        A basic descriptor for NEF.

        Parameters
        ----------
        magic : int, default=0
            Magic number for model_nef_descriptor (0x5AA55AA5).
        metadata : ModelNefMetadata, default=ModelNefMetadata()
            NEF metadata.
        target_chip : ModelTargetChip, default=ModelTargetChip.KP_MODEL_TARGET_CHIP_UNKNOWN
            Target chip of all models.
        crc : int, default=0
            CRC of all models.
        models : List[SingleModelDescriptor], default=[]
            Model descriptors.
        """
        ValueBase.__init__(self,
                           element_buffer_class=ModelNefDescriptorBuffer,
                           magic=magic,
                           metadata=ModelNefMetadataBuffer(
                               kn_num=metadata.kn_number,
                               toolchain_version=metadata.toolchain_version,
                               compiler_version=metadata.compiler_version,
                               nef_schema_version=BaseVersionBuffer(major=metadata.nef_schema_version.major,
                                                                    minor=metadata.nef_schema_version.minor,
                                                                    revision=metadata.nef_schema_version.revision),
                               platform=metadata.platform
                           ),
                           target=target_chip.value,
                           crc=crc,
                           num_models=len(models),
                           models=[
                               SingleModelDescriptorBuffer(
                                   target=model.target_chip.value,
                                   version=model.version,
                                   id=model.id,
                                   input_nodes_num=len(model.input_nodes),
                                   input_nodes=SingleModelDescriptor._construct_tensor_descriptor_buffer_list(
                                       tensor_descriptor_list=model.input_nodes),
                                   output_nodes_num=len(model.output_nodes),
                                   output_nodes=SingleModelDescriptor._construct_tensor_descriptor_buffer_list(
                                       tensor_descriptor_list=model.output_nodes),
                                   setup_bin_schema_version=BaseVersionBuffer(
                                       major=model.setup_schema_version.major,
                                       minor=model.setup_schema_version.minor,
                                       revision=model.setup_schema_version.revision
                                   ),
                                   file_schema_version=BaseVersionBuffer(
                                       major=model.setup_file_schema_version.major,
                                       minor=model.setup_file_schema_version.minor,
                                       revision=model.setup_file_schema_version.revision
                                   ),
                                   max_raw_out_size=model.max_raw_out_size
                               ) for model in models
                           ])

    def __del__(self):
        if self._is_allocate_from_c:
            status = wrapper_utils.release_model_nef_descriptor(self._element_buffer)
            self._is_allocate_from_c = False

            if ApiReturnCode.KP_SUCCESS != ApiReturnCode(status):
                raise MemoryError(
                    'ModelNefDescriptor auto release fail. Error Code - {}'.format(str(ApiReturnCode(status))))

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def magic(self) -> int:
        """
        int: Magic number for model_nef_descriptor (0x5AA55AA5).
        """
        return self._element_buffer._magic

    @property
    def metadata(self) -> ModelNefMetadata:
        """
        ModelNefMetadata: NEF metadata.
        """
        return ModelNefMetadata(
            kn_number=self._element_buffer._metadata._kn_num,
            toolchain_version=self._element_buffer._metadata._toolchain_version.decode("utf-8"),
            compiler_version=self._element_buffer._metadata._compiler_version.decode("utf-8"),
            nef_schema_version=NefSchemaVersion(
                major=self._element_buffer._metadata._nef_schema_version._major,
                minor=self._element_buffer._metadata._nef_schema_version._minor,
                revision=self._element_buffer._metadata._nef_schema_version._revision
            ),
            platform=self._element_buffer._metadata._platform.decode("utf-8")
        )

    @property
    def target_chip(self) -> ModelTargetChip:
        """
        ModelTargetChip: Target chip of all models.
        """
        return ModelTargetChip(self._element_buffer._target)

    @property
    def crc(self) -> int:
        """
        int: CRC of NEF models.
        """
        return self._element_buffer._crc

    @property
    def models(self) -> List[SingleModelDescriptor]:
        """
        List[SingleModelDescriptor]: Model descriptors.
        """
        return [
            SingleModelDescriptor(
                target_chip=ModelTargetChip(model_buffer._target),
                version=model_buffer._version,
                id=model_buffer._id,
                input_nodes=SingleModelDescriptor._construct_tensor_descriptor_list(
                    tensor_descriptor_buffer_list=model_buffer._input_nodes[:model_buffer._input_nodes_num]),
                output_nodes=SingleModelDescriptor._construct_tensor_descriptor_list(
                    tensor_descriptor_buffer_list=model_buffer._output_nodes[:model_buffer._output_nodes_num]),
                setup_schema_version=SetupSchemaVersion(
                    major=model_buffer._setup_bin_schema_version._major,
                    minor=model_buffer._setup_bin_schema_version._minor,
                    revision=model_buffer._setup_bin_schema_version._revision
                ),
                setup_file_schema_version=SetupFileSchemaVersion(
                    major=model_buffer._file_schema_version._major,
                    minor=model_buffer._file_schema_version._minor,
                    revision=model_buffer._file_schema_version._revision
                ),
                max_raw_out_size=model_buffer._max_raw_out_size
            ) for model_buffer in self._element_buffer._models[:self._element_buffer._num_models]
        ]

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'magic': '0x{:X}'.format(self.magic),
            'metadata': self.metadata.get_member_variable_dict(),
            'target_chip': str(self.target_chip),
            'crc': '0x{:X}'.format(self.crc),
            'models': {},
        }

        for idx, model in enumerate(self.models):
            member_variable_dict['models'][idx] = model.get_member_variable_dict()

        return member_variable_dict


class DdrManageAttributes(ValueBase, ValueRepresentBase):
    """
    DDR memory management descriptor of Kneron device.

    Attributes
    ----------
    model_size : int, default=0
        DDR space for model.
    input_buffer_size : int, default=0
        Input buffer size for FIFO queue.
    input_buffer_count : int, default=0
        Input buffer count for FIFO queue.
    result_buffer_size : kp.ModelNefDescriptor, default=kp.ModelNefDescriptor()
        Result buffer size for FIFO queue.
    result_buffer_count : int, default=0
        Result buffer count for FIFO queue.
    """

    def __init__(self,
                 model_size: int = 0,
                 input_buffer_size: int = 0,
                 input_buffer_count: int = 0,
                 result_buffer_size: int = 0,
                 result_buffer_count: int = 0):
        """
        DDR memory management descriptor of Kneron device.

        Attributes
        ----------
        model_size : int, default=0
            DDR space for model.
        input_buffer_size : int, default=0
            Input buffer size for FIFO queue.
        input_buffer_count : int, default=0
            Input buffer count for FIFO queue.
        result_buffer_size : kp.ModelNefDescriptor, default=kp.ModelNefDescriptor()
            Result buffer size for FIFO queue.
        result_buffer_count : int, default=0
            Result buffer count for FIFO queue.
        """
        ValueBase.__init__(self,
                           element_buffer_class=DdrManageAttributesBuffer,
                           model_size=model_size,
                           input_buffer_size=input_buffer_size,
                           input_buffer_count=input_buffer_count,
                           result_buffer_size=result_buffer_size,
                           result_buffer_count=result_buffer_count)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def model_size(self) -> int:
        """
        int: DDR space for model.
        """
        return self._element_buffer._model_size

    @property
    def input_buffer_size(self) -> int:
        """
        int: Input buffer size for FIFO queue.
        """
        return self._element_buffer._input_buffer_size

    @property
    def input_buffer_count(self) -> int:
        """
        int: Input buffer count for FIFO queue.
        """
        return self._element_buffer._input_buffer_count

    @property
    def result_buffer_size(self) -> int:
        """
        int: Result buffer size for FIFO queue.
        """
        return self._element_buffer._result_buffer_size

    @property
    def result_buffer_count(self) -> int:
        """
        int: Result buffer count for FIFO queue.
        """
        return self._element_buffer._result_buffer_count

    def get_member_variable_dict(self) -> dict:
        return {
            'model_size (MiB)': self.model_size,
            'input_buffer_size': self.input_buffer_size,
            'input_buffer_count': self.input_buffer_count,
            'result_buffer_size': self.result_buffer_size,
            'result_buffer_count': self.result_buffer_count
        }


class DeviceGroupContent(ValueBase, ValueRepresentBase):
    """
    A DeviceGroup descriptor.

    Attributes
    ----------
    timeout : int, default=0
        Global timeout value for all USB communications with the device.
    num_device : int, default=0
        Number of devices in device group.
    product_id : int, default=0
        USB PID (Product ID).
    loaded_model_nef_descriptor : kp.ModelNefDescriptor, default=kp.ModelNefDescriptor()
        ModelNefDescriptor object for describing the uploaded models.
    ddr_manage_attributes : kp.DdrManageAttributes, default=kp.DdrManageAttributes()
        Attributes for DDR memory management.
    """

    def __init__(self,
                 timeout: int = 0,
                 num_device: int = 0,
                 product_id: int = 0,
                 loaded_model_nef_descriptor: ModelNefDescriptor = ModelNefDescriptor(),
                 ddr_manage_attributes: DdrManageAttributes = DdrManageAttributes()):
        """
        A DeviceGroup descriptor.

        Attributes
        ----------
        timeout : int, default=0
            Global timeout value for all USB communications with the device.
        num_device : int, default=0
            Number of devices in device group.
        product_id : int, default=0
            USB PID (Product ID).
        loaded_model_nef_descriptor : kp.ModelNefDescriptor, default=kp.ModelNefDescriptor()
            ModelNefDescriptor object for describing the uploaded models.
        ddr_manage_attributes : kp.DdrManageAttributes, default=kp.DdrManageAttributes()
            Attributes for DDR memory management.
        """
        ValueBase.__init__(self,
                           element_buffer_class=DeviceGroupBuffer,
                           timeout=timeout,
                           num_device=num_device,
                           product_id=product_id,
                           loaded_model_nef_descriptor=ModelNefDescriptorBuffer(
                               magic=loaded_model_nef_descriptor.magic,
                               metadata=ModelNefMetadataBuffer(
                                   kn_num=loaded_model_nef_descriptor.metadata.kn_number,
                                   toolchain_version=loaded_model_nef_descriptor.metadata.toolchain_version,
                                   compiler_version=loaded_model_nef_descriptor.metadata.compiler_version,
                                   nef_schema_version=BaseVersionBuffer(major=loaded_model_nef_descriptor.metadata.nef_schema_version.major,
                                                                        minor=loaded_model_nef_descriptor.metadata.nef_schema_version.minor,
                                                                        revision=loaded_model_nef_descriptor.metadata.nef_schema_version.revision),
                                   platform=loaded_model_nef_descriptor.metadata.platform
                               ),
                               target=loaded_model_nef_descriptor.target_chip.value,
                               crc=loaded_model_nef_descriptor.crc,
                               num_models=len(loaded_model_nef_descriptor.models),
                               models=[
                                   SingleModelDescriptorBuffer(
                                       target=model.target_chip.value,
                                       version=model.version,
                                       id=model.id,
                                       input_nodes_num=len(model.input_nodes),
                                       input_nodes=SingleModelDescriptor._construct_tensor_descriptor_buffer_list(
                                           tensor_descriptor_list=model.input_nodes),
                                       output_nodes_num=len(model.output_nodes),
                                       output_nodes=SingleModelDescriptor._construct_tensor_descriptor_buffer_list(
                                           tensor_descriptor_list=model.output_nodes),
                                       setup_bin_schema_version=BaseVersionBuffer(
                                           major=model.setup_schema_version.major,
                                           minor=model.setup_schema_version.minor,
                                           revision=model.setup_schema_version.revision
                                       ),
                                       file_schema_version=BaseVersionBuffer(
                                           major=model.setup_file_schema_version.major,
                                           minor=model.setup_file_schema_version.minor,
                                           revision=model.setup_file_schema_version.revision
                                       ),
                                       max_raw_out_size=model.max_raw_out_size
                                   ) for model in loaded_model_nef_descriptor.models
                               ]
                           ),
                           ddr_manage_attributes=ddr_manage_attributes._get_element_buffer())

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def timeout(self) -> int:
        """
        int: Memory address of connected Kneron device handler.
        """
        return self._element_buffer._timeout

    @property
    def num_device(self) -> int:
        """
        int: Memory address of connected Kneron device handler.
        """
        return self._element_buffer._num_device

    @property
    def product_id(self) -> int:
        """
        int: Memory address of connected Kneron device handler.
        """
        return self._element_buffer._product_id

    @property
    def loaded_model_nef_descriptor(self) -> ModelNefDescriptor:
        """
        ModelNefDescriptor: Memory address of connected Kneron device handler.
        """
        _loaded_model_desc = self._element_buffer._loaded_model_desc

        if 0x5AA55AA5 == _loaded_model_desc._magic:
            # model is loaded
            model_nef_descriptor =  ModelNefDescriptor(
                magic=_loaded_model_desc._magic,
                metadata=ModelNefMetadata(
                    kn_number=_loaded_model_desc._metadata._kn_num,
                    toolchain_version=_loaded_model_desc._metadata._toolchain_version.decode("utf-8"),
                    compiler_version=_loaded_model_desc._metadata._compiler_version.decode("utf-8"),
                    nef_schema_version=NefSchemaVersion(
                        major=_loaded_model_desc._metadata._nef_schema_version._major,
                        minor=_loaded_model_desc._metadata._nef_schema_version._minor,
                        revision=_loaded_model_desc._metadata._nef_schema_version._revision
                    ),
                    platform=_loaded_model_desc._metadata._platform.decode("utf-8")
                ),
                target_chip=ModelTargetChip(_loaded_model_desc._target),
                crc=_loaded_model_desc._crc,
                models=[
                    SingleModelDescriptor(
                        target_chip=ModelTargetChip(model_buffer._target),
                        version=model_buffer._version,
                        id=model_buffer._id,
                        input_nodes=SingleModelDescriptor._construct_tensor_descriptor_list(
                            tensor_descriptor_buffer_list=model_buffer._input_nodes[:model_buffer._input_nodes_num]),
                        output_nodes=SingleModelDescriptor._construct_tensor_descriptor_list(
                            tensor_descriptor_buffer_list=model_buffer._output_nodes[:model_buffer._output_nodes_num]),
                        setup_schema_version=SetupSchemaVersion(
                            major=model_buffer._setup_bin_schema_version._major,
                            minor=model_buffer._setup_bin_schema_version._minor,
                            revision=model_buffer._setup_bin_schema_version._revision
                        ),
                        setup_file_schema_version=SetupFileSchemaVersion(
                            major=model_buffer._file_schema_version._major,
                            minor=model_buffer._file_schema_version._minor,
                            revision=model_buffer._file_schema_version._revision
                        ),
                        max_raw_out_size=model_buffer._max_raw_out_size
                    ) for model_buffer in _loaded_model_desc._models[:_loaded_model_desc._num_models]
                ]
            )
        else:
            # model is not loaded
            model_nef_descriptor = ModelNefDescriptor()

        return model_nef_descriptor

    @property
    def ddr_manage_attributes(self) -> DdrManageAttributes:
        """
        DdrManageAttributes: Memory address of connected Kneron device handler.
        """
        _ddr_attr = self._element_buffer._ddr_attr

        return DdrManageAttributes(
            model_size=_ddr_attr._model_size,
            input_buffer_size=_ddr_attr._input_buffer_size,
            input_buffer_count=_ddr_attr._input_buffer_count,
            result_buffer_size=_ddr_attr._result_buffer_size,
            result_buffer_count=_ddr_attr._result_buffer_count
        )

    def get_member_variable_dict(self) -> dict:
        return {
            'timeout': self.timeout,
            'num_device': self.num_device,
            'product_id': '0x{:X}'.format(self.product_id),
            'loaded_model_nef_descriptor': self.loaded_model_nef_descriptor.get_member_variable_dict(),
            'ddr_manage_attributes': self.ddr_manage_attributes.get_member_variable_dict()
        }


class DeviceGroup(ValueBase, ValueRepresentBase):
    """
    A handle represent connected Kneron device.

    Attributes
    ----------
    address : int
        Memory address of connected Kneron device handler.
    """

    def __init__(self,
                 address: int):
        """
        A handle represent connected Kneron device.

        Parameters
        ----------
        address : int
            Memory address of connected Kneron device handler.
        """
        ValueBase.__init__(self,
                           element_buffer_class=DeviceGroupPointerBuffer,
                           address=address)

    def __del__(self):
        if self._is_allocate_from_c:
            status = wrapper_utils.disconnect_devices_by_address(self._element_buffer._address)
            self._is_allocate_from_c = False

            if ApiReturnCode.KP_SUCCESS != ApiReturnCode(status):
                raise MemoryError(
                    'DeviceGroup auto disconnect fail. Error Code - {}'.format(str(ApiReturnCode(status))))

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def address(self) -> int:
        """
        int: Memory address of connected Kneron device handler.
        """
        return self._element_buffer._address

    @property
    def content(self) -> DeviceGroupContent:
        """
        DeviceGroupContent: A DeviceGroup descriptor.
        """
        device_group_content = DeviceGroupContent()
        device_group_content._element_buffer = DeviceGroupBuffer.from_address(self._element_buffer._address)

        return device_group_content

    def get_member_variable_dict(self) -> dict:
        return {
            'device_group_address ': '0x{:X}'.format(self.address),
            'content': self.content.get_member_variable_dict()
        }


class FirmwareVersion(ValueBase, ValueRepresentBase):
    """
    Information of firmware version.

    Attributes
    ----------
    reserved : int, default=0
        Reserved version number for backward compatibility.
    major : int, default=0
    minor : int, default=0
    update : int, default=0
    build : int, default=0
    """

    def __init__(self,
                 reserved: int = 0,
                 major: int = 0,
                 minor: int = 0,
                 update: int = 0,
                 build: int = 0):
        """
        Information of firmware version.

        Parameters
        ----------
        reserved : int, default=0
            Reserved version number for backward compatibility.
        major : int, default=0
        minor : int, default=0
        update : int, default=0
        build : int, default=0
        """
        ValueBase.__init__(self,
                           element_buffer_class=FirmwareVersionBuffer,
                           reserved=reserved,
                           major=major,
                           minor=minor,
                           update=update,
                           build=build)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def reserved(self) -> int:
        """
        int: Reserved version number for backward compatibility.
        """
        return self._element_buffer._reserved

    @property
    def major(self) -> int:
        return self._element_buffer._major

    @property
    def minor(self) -> int:
        return self._element_buffer._minor

    @property
    def update(self) -> int:
        return self._element_buffer._update

    @property
    def build(self) -> int:
        return self._element_buffer._build

    def get_member_variable_dict(self) -> dict:
        if self.reserved == 0:
            return {
                'firmware_version': '{}.{}.{}-build.{}'.format(
                    self.major,
                    self.minor,
                    self.update,
                    self.build
                )
            }
        else:
            return {
                'firmware_version': '{}.{}.{}.{}-build.{}'.format(
                    self.reserved,
                    self.major,
                    self.minor,
                    self.update,
                    self.build
                )
            }


class SystemInfo(ValueBase, ValueRepresentBase):
    """
    System Information of Kneron device.

    Attributes
    ----------
    kn_number : int, default=0
        Unique Kneron device ID.
    firmware_version : kp.FirmwareVersion, default=kp.FirmwareVersion()
        Firmware version of Kneron device.
    """

    def __init__(self,
                 kn_number: int = 0,
                 firmware_version: FirmwareVersion = FirmwareVersion()):
        """
        System Information of Kneron device.

        Parameters
        ----------
        kn_number : int, default=0
            Unique Kneron device ID.
        firmware_version : kp.FirmwareVersion, default=kp.FirmwareVersion()
            Firmware version of Kneron device.
        """
        ValueBase.__init__(self,
                           element_buffer_class=SystemInfoBuffer,
                           kn_number=kn_number,
                           firmware_version=firmware_version._get_element_buffer())

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def kn_number(self):
        """
        int: Unique Kneron device ID.
        """
        return self._element_buffer._kn_number

    @property
    def firmware_version(self):
        """
        kp.FirmwareVersion: Firmware version of Kneron device.
        """
        return FirmwareVersion(
            reserved=self._element_buffer._firmware_version._reserved,
            major=self._element_buffer._firmware_version._major,
            minor=self._element_buffer._firmware_version._minor,
            update=self._element_buffer._firmware_version._update,
            build=self._element_buffer._firmware_version._build
        )

    def get_member_variable_dict(self) -> dict:
        return {
            'kn_number': '0x{:X}'.format(self.kn_number),
            'firmware_version': self.firmware_version.get_member_variable_dict()['firmware_version'],
        }


class InferenceConfiguration(ValueBase, ValueRepresentBase):
    """
    Inference configurations.

    Attributes
    ----------
    enable_frame_drop : bool, default=False
        Enable this to keep inference non-blocking by dropping oldest and unprocessed frames.
    """

    def __init__(self,
                 enable_frame_drop: bool = False):
        """
        Inference configurations.

        Parameters
        ----------
        enable_frame_drop : bool, default=False
            Enable this to keep inference non-blocking by dropping oldest and unprocessed frames.
        """
        ValueBase.__init__(self,
                           element_buffer_class=InfConfigurationBuffer,
                           enable_frame_drop=enable_frame_drop)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def enable_frame_drop(self) -> bool:
        """
        bool: Enable this to keep inference non-blocking by dropping oldest and unprocessed frames.
        """
        return self._element_buffer._enable_frame_drop

    @enable_frame_drop.setter
    def enable_frame_drop(self, value: bool):
        self._element_buffer._enable_frame_drop = value

    def get_member_variable_dict(self) -> dict:
        return {
            'enable_frame_drop': self.enable_frame_drop
        }


class InferenceCropBox(ValueBase, ValueRepresentBase):
    """
    Class for an image crop region.

    Attributes
    ----------
    crop_box_index : int, default=0
        Index number of crop box.
    x : int, default=0
        X coordinate of crop box top-left corner.
    y : int, default=0
        Y coordinate of crop box top-left corner.
    width : int, default=0
        Width coordinate of crop box.
    height : int, default=0
        Height coordinate of crop box.
    """

    def __init__(self,
                 crop_box_index: int = 0,
                 x: int = 0,
                 y: int = 0,
                 width: int = 0,
                 height: int = 0):
        """
        Class for an image crop region.

        Parameters
        ----------
        crop_box_index : int, default=0
            Index number of crop box.
        x : int, default=0
            X coordinate of crop box top-left corner.
        y : int, default=0
            Y coordinate of crop box top-left corner.
        width : int, default=0
            Width coordinate of crop box.
        height : int, default=0
            Height coordinate of crop box.
        """
        ValueBase.__init__(self,
                           element_buffer_class=InfCropBoxBuffer,
                           crop_box_index=crop_box_index,
                           x=x,
                           y=y,
                           width=width,
                           height=height)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def crop_box_index(self) -> int:
        """
        int: Index number of crop box.
        """
        return self._element_buffer._crop_number

    @crop_box_index.setter
    def crop_box_index(self, value: int):
        self._element_buffer._crop_number = value

    @property
    def x(self) -> int:
        """
        int: X coordinate of crop box top-left corner.
        """
        return self._element_buffer._x1

    @x.setter
    def x(self, value: int):
        self._element_buffer._x1 = value

    @property
    def y(self) -> int:
        """
        int: Y coordinate of crop box top-left corner.
        """
        return self._element_buffer._y1

    @y.setter
    def y(self, value: int):
        self._element_buffer._y1 = value

    @property
    def width(self) -> int:
        """
        int: Width coordinate of crop box.
        """
        return self._element_buffer._width

    @width.setter
    def width(self, value: int):
        self._element_buffer._width = value

    @property
    def height(self) -> int:
        """
        int: Height coordinate of crop box.
        """
        return self._element_buffer._height

    @height.setter
    def height(self, value: int):
        self._element_buffer._height = value

    def get_member_variable_dict(self) -> dict:
        return {
            'crop_box_index': self.crop_box_index,
            'x': self.x,
            'y': self.y,
            'width': self.width,
            'height': self.height
        }


class HwPreProcInfo(ValueBase, ValueRepresentBase):
    """
    Information of Hardware Pre Process.

    Attributes
    ----------
    img_width : int, default=0
        Image width before hardware pre-process.
    img_height: int, default=0
        Image height before hardware pre-process.
    resized_img_width: int, default=0
        Image width after resize.
    resized_img_height: int, default=0
        Image height after resize.
    pad_top: int, default=0
        Pixels padding on top.
    pad_bottom: int, default=0
        Pixels padding on bottom.
    pad_left: int, default=0
        Pixels padding on left.
    pad_right: int, default=0
        Pixels padding on right.
    model_input_width: int, default=0
        Model required input width.
    model_input_height: int, default=0
        Model required input height.
    crop_area: InferenceCropBox, default=InferenceCropBox()
        Information of crop area. (may not be the same as input due to hardware limitation)
    """

    def __init__(self,
                 img_width: int = 0,
                 img_height: int = 0,
                 resized_img_width: int = 0,
                 resized_img_height: int = 0,
                 pad_top: int = 0,
                 pad_bottom: int = 0,
                 pad_left: int = 0,
                 pad_right: int = 0,
                 model_input_width: int = 0,
                 model_input_height: int = 0,
                 crop_area: InferenceCropBox = InferenceCropBox()):
        """
        Information of firmware version.

        Parameters
        ----------
        img_width : int, default=0
            Image width before hardware pre-process.
        img_height: int, default=0
            Image height before hardware pre-process.
        resized_img_width: int, default=0
            Image width after resize.
        resized_img_height: int, default=0
            Image height after resize.
        pad_top: int, default=0
            Pixels padding on top.
        pad_bottom: int, default=0
            Pixels padding on bottom.
        pad_left: int, default=0
            Pixels padding on left.
        pad_right: int, default=0
            Pixels padding on right.
        model_input_width: int, default=0
            Model required input width.
        model_input_height: int, default=0
            Model required input height.
        crop_area: InferenceCropBox, default=InferenceCropBox()
            Information of crop area. (may not be the same as input due to hardware limitation)
        """
        ValueBase.__init__(self,
                           element_buffer_class=HwPreProcInfoBuffer,
                           img_width=img_width,
                           img_height=img_height,
                           resized_img_width=resized_img_width,
                           resized_img_height=resized_img_height,
                           pad_top=pad_top,
                           pad_bottom=pad_bottom,
                           pad_left=pad_left,
                           pad_right=pad_right,
                           model_input_width=model_input_width,
                           model_input_height=model_input_height,
                           crop_area=InfCropBoxBuffer(
                               crop_box_index=crop_area.crop_box_index,
                               x=crop_area.x,
                               y=crop_area.y,
                               width=crop_area.width,
                               height=crop_area.height
                           ))

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def img_width(self) -> int:
        """
        int: Image width before hardware pre-process.
        """
        return self._element_buffer._img_width

    @property
    def img_height(self) -> int:
        """
        int: Image height before hardware pre-process.
        """
        return self._element_buffer._img_height

    @property
    def resized_img_width(self) -> int:
        """
        int: Image width after resize.
        """
        return self._element_buffer._resized_img_width

    @property
    def resized_img_height(self) -> int:
        """
        int: Image height after resize.
        """
        return self._element_buffer._resized_img_height

    @property
    def pad_top(self) -> int:
        """
        int: Pixels padding on top.
        """
        return self._element_buffer._pad_top

    @property
    def pad_bottom(self) -> int:
        """
        int: Pixels padding on bottom.
        """
        return self._element_buffer._pad_bottom

    @property
    def pad_left(self) -> int:
        """
        int: Pixels padding on left.
        """
        return self._element_buffer._pad_left

    @property
    def pad_right(self) -> int:
        """
        int: Pixels padding on right.
        """
        return self._element_buffer._pad_right

    @property
    def model_input_width(self) -> int:
        """
        int: Model required input width.
        """
        return self._element_buffer._model_input_width

    @property
    def model_input_height(self) -> int:
        """
        int: Model required input height.
        """
        return self._element_buffer._model_input_height

    @property
    def crop_area(self) -> InferenceCropBox:
        """
        InferenceCropBox: Information of crop area. (may not be the same as input due to hardware limitation)
        """
        return InferenceCropBox(
            crop_box_index=self._element_buffer._crop_area._crop_number,
            x=self._element_buffer._crop_area._x1,
            y=self._element_buffer._crop_area._y1,
            width=self._element_buffer._crop_area._width,
            height=self._element_buffer._crop_area._height
        )

    def get_member_variable_dict(self) -> dict:
        return {
            'img_width': self.img_width,
            'img_height': self.img_height,
            'resized_img_width': self.resized_img_width,
            'resized_img_height': self.resized_img_height,
            'pad_top': self.pad_top,
            'pad_bottom': self.pad_bottom,
            'pad_left': self.pad_left,
            'pad_right': self.pad_right,
            'model_input_width': self.model_input_width,
            'model_input_height': self.model_input_height,
            'crop_area': self.crop_area.get_member_variable_dict()
        }


class GenericRawResultNDArray(ValueBase, ValueRepresentBase):
    """
    Inference raw result buffer.

    Attributes
    ----------
    buffer_size : int
        Size of generic inference raw result buffer.
    """

    def __init__(self, buffer_size: int):
        """
        Generic inference raw result buffer.

        Parameters
        ----------
        buffer_size : int
            Size of generic inference raw result buffer.
        """
        ValueBase.__init__(self,
                           element_buffer_class=GenericRawResultNDArrayBuffer,
                           buffer_size=buffer_size)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def buffer_size(self) -> int:
        """
        int: Size of generic inference raw result buffer.
        """
        return self._element_buffer._buffer_size

    def get_member_variable_dict(self) -> dict:
        return {
            'buffer_size': self.buffer_size
        }


class GenericInputNodeImage(ValueBase, ValueRepresentBase):
    """
    Single inference image data descriptor.

    Attributes
    ----------
    image : bytes, numpy.ndarray, default=numpy.ndarray([0, 0, kp.Const.CHANNEL_NUM_OTHER_FORMAT.value], dtype=np.uint8)
        The data bytes or numpy.ndarray (dtype=numpy.uint8, dim=3) contains the image.
    width : int, default=0
        Inference image width (Must apply when using bytes image data).
    height : int, default=0
        Inference image height (Must apply when using bytes image data).
    image_format : kp.ImageFormat, default=kp.ImageFormat.KP_IMAGE_FORMAT_RGB565
        Inference image format, refer to ImageFormat.
    resize_mode : kp.ResizeMode, default=kp.ResizeMode.KP_RESIZE_ENABLE
        Preprocess resize mode, refer to ResizeMode.
    padding_mode : kp.PaddingMode, default=kp.PaddingMode.KP_PADDING_CORNER
        Preprocess padding mode, refer to PaddingMode.
    normalize_mode : kp.NormalizeMode, default=kp.NormalizeMode.KP_NORMALIZE_KNERON
        Inference normalization, refer to NormalizeMode.
    inference_crop_box_list : List[kp.InferenceCropBox], default=[]
        Box information to crop.
    """

    def __init__(self,
                 image: Union[bytes, np.ndarray] = np.ndarray([0, 0, Const.CHANNEL_NUM_OTHER_FORMAT.value],
                                                              dtype=np.uint8),
                 width: int = 0,
                 height: int = 0,
                 image_format: ImageFormat = ImageFormat.KP_IMAGE_FORMAT_RGB565,
                 resize_mode: ResizeMode = ResizeMode.KP_RESIZE_ENABLE,
                 padding_mode: PaddingMode = PaddingMode.KP_PADDING_CORNER,
                 normalize_mode: NormalizeMode = NormalizeMode.KP_NORMALIZE_KNERON,
                 inference_crop_box_list: List[InferenceCropBox] = []):
        """
        Single inference image data descriptor.

        Attributes
        ----------
        image : bytes, numpy.ndarray, default=numpy.ndarray([0, 0, kp.Const.CHANNEL_NUM_OTHER_FORMAT.value], dtype=np.uint8)
            The data bytes or numpy.ndarray (dtype=numpy.uint8, dim=3) contains the image.
        width : int, default=0
            Inference image width (Must apply when using bytes image data).
        height : int, default=0
            Inference image height (Must apply when using bytes image data).
        image_format : kp.ImageFormat, default=kp.ImageFormat.KP_IMAGE_FORMAT_RGB565
            Inference image format, refer to ImageFormat.
        resize_mode : kp.ResizeMode, default=kp.ResizeMode.KP_RESIZE_ENABLE
            Preprocess resize mode, refer to ResizeMode.
        padding_mode : kp.PaddingMode, default=kp.PaddingMode.KP_PADDING_CORNER
            Preprocess padding mode, refer to PaddingMode.
        normalize_mode : kp.NormalizeMode, default=kp.NormalizeMode.KP_NORMALIZE_KNERON
            Inference normalization, refer to NormalizeMode.
        inference_crop_box_list : List[kp.InferenceCropBox], default=[]
            Box information to crop.
        """
        if isinstance(image, np.ndarray):
            if np.uint8 != image.dtype:
                _check_api_return_code(result='invalid dtype {} for input image numpy.ndarray'.format(image.dtype),
                                       api_return_code=ApiReturnCode(ApiReturnCode.KP_ERROR_INVALID_PARAM_12))

            if 3 == image.ndim:
                if GenericInputNodeImage._get_channel_from_image_format(image_format) != image.shape[2]:
                    _check_api_return_code(
                        result='invalid channel {} for input image numpy.ndarray, the input image numpy.ndarray must be {} for image formate {}.'.format(
                            image.shape[2],
                            GenericInputNodeImage._get_channel_from_image_format(image_format),
                            str(image_format)
                        ),
                        api_return_code=ApiReturnCode(ApiReturnCode.KP_ERROR_INVALID_PARAM_12))
            elif 2 == image.ndim:
                if ImageFormat.KP_IMAGE_FORMAT_RAW8 != image_format:
                    _check_api_return_code(
                        result='invalid channel {} for input image numpy.ndarray, the input image numpy.ndarray must be {} for image formate {}.'.format(
                            1,
                            GenericInputNodeImage._get_channel_from_image_format(image_format),
                            str(image_format)
                        ),
                        api_return_code=ApiReturnCode(ApiReturnCode.KP_ERROR_INVALID_PARAM_12))
            else:
                _check_api_return_code(
                    result='invalid dimension {} for input image numpy.ndarray, the input image numpy.ndarray must be 2 or 3.'.format(
                        image.ndim),
                    api_return_code=ApiReturnCode(ApiReturnCode.KP_ERROR_INVALID_PARAM_12))

            width = image.shape[1]
            height = image.shape[0]
            image = image.tobytes()

        ValueBase.__init__(self,
                           element_buffer_class=GenericInputNodeImageBuffer,
                           width=width,
                           height=height,
                           resize_mode=resize_mode,
                           padding_mode=padding_mode,
                           image_format=image_format,
                           normalize_mode=normalize_mode,
                           inference_crop_box_buffer_list=[
                               InfCropBoxBuffer(
                                   crop_box_index=inference_crop_box.crop_box_index,
                                   x=inference_crop_box.x,
                                   y=inference_crop_box.y,
                                   width=inference_crop_box.width,
                                   height=inference_crop_box.height
                               ) for inference_crop_box in inference_crop_box_list
                           ],
                           image_buffer=image)

    def _cast_element_buffer(self) -> None:
        pass

    @staticmethod
    def _get_channel_from_image_format(image_format: ImageFormat) -> int:
        if ImageFormat.KP_IMAGE_FORMAT_RGBA8888 == image_format:
            return Const.CHANNEL_NUM_RGBA8888.value
        elif ImageFormat.KP_IMAGE_FORMAT_RAW8 == image_format:
            return Const.CHANNEL_NUM_RAW8.value
        else:
            return Const.CHANNEL_NUM_OTHER_FORMAT.value

    @property
    def image(self) -> np.ndarray:
        """
        numpy.ndarray: The data bytes or numpy.ndarray (dtype=numpy.uint8, dim=3) contains the image.
        """
        if 0 == self.height or 0 == self.width:
            return np.zeros(shape=[self.height, self.width,
                                   GenericInputNodeImage._get_channel_from_image_format(self.image_format)],
                            dtype=np.uint8)
        else:
            return np.ctypeslib.as_array(self._element_buffer._image_buffer, shape=[self.height, self.width,
                                                                                    GenericInputNodeImage._get_channel_from_image_format(
                                                                                        self.image_format)])

    @image.setter
    def image(self, value: Union[bytes, np.ndarray]):
        if isinstance(value, np.ndarray):
            if np.uint8 != value.dtype:
                _check_api_return_code(result='invalid dtype {} for input image numpy.ndarray'.format(value.dtype),
                                       api_return_code=ApiReturnCode(ApiReturnCode.KP_ERROR_INVALID_PARAM_12))

            if 3 != value.ndim or 2 != value.ndim:
                _check_api_return_code(
                    result='invalid dimension {} for input image numpy.ndarray, the input image numpy.ndarray must be 2 or 3.'.format(
                        value.ndim),
                    api_return_code=ApiReturnCode(ApiReturnCode.KP_ERROR_INVALID_PARAM_12))

            self._element_buffer._width = value.shape[1]
            self._element_buffer._height = value.shape[0]
            value = value.tobytes()

        self._element_buffer._image_buffer = (ctypes.c_ubyte * len(value)).from_buffer(bytearray(value))

    @property
    def width(self) -> int:
        """
        int: Inference image width (Must apply when using bytes image data).
        """
        return self._element_buffer._width

    @width.setter
    def width(self, value: int):
        self._element_buffer._width = value

    @property
    def height(self) -> int:
        """
        int: Inference image height (Must apply when using bytes image data).
        """
        return self._element_buffer._height

    @height.setter
    def height(self, value: int):
        self._element_buffer._height = value

    @property
    def image_format(self) -> ImageFormat:
        """
        kp.ImageFormat: Inference image format, refer to ImageFormat.
        """
        return ImageFormat(self._element_buffer._image_format)

    @image_format.setter
    def image_format(self, value: ImageFormat):
        self._element_buffer._image_format = value.value

    @property
    def resize_mode(self) -> ResizeMode:
        """
        kp.ResizeMode: Preprocess resize mode, refer to ResizeMode.
        """
        return ResizeMode(self._element_buffer._resize_mode)

    @resize_mode.setter
    def resize_mode(self, value: ResizeMode):
        self._element_buffer._resize_mode = value.value

    @property
    def padding_mode(self) -> PaddingMode:
        """
        kp.PaddingMode: Preprocess padding mode, none or auto refer to PaddingMode.
        """
        return PaddingMode(self._element_buffer._padding_mode)

    @padding_mode.setter
    def padding_mode(self, value: PaddingMode):
        self._element_buffer._padding_mode = value.value

    @property
    def normalize_mode(self) -> NormalizeMode:
        """
        kp.NormalizeMode: Inference normalization, refer to NormalizeMode.
        """
        return NormalizeMode(self._element_buffer._normalize_mode)

    @normalize_mode.setter
    def normalize_mode(self, value: NormalizeMode):
        self._element_buffer._normalize_mode = value.value

    @property
    def crop_count(self) -> int:
        """
        int: Number of crop box.
        """
        return self._element_buffer._crop_count

    @property
    def inference_crop_box_list(self) -> List[InferenceCropBox]:
        """
        List[kp.InferenceCropBox]: Box information to crop.
        """
        inference_crop_box_buffer_list = list(self._element_buffer._inf_crop)[:self.crop_count]

        inference_crop_box_list = [InferenceCropBox(
            crop_box_index=inference_crop_box_buffer._crop_number,
            x=inference_crop_box_buffer._x1,
            y=inference_crop_box_buffer._y1,
            width=inference_crop_box_buffer._width,
            height=inference_crop_box_buffer._height
        ) for inference_crop_box_buffer in inference_crop_box_buffer_list]

        return inference_crop_box_list

    # todo. not allow user setting it -> private it.
    @inference_crop_box_list.setter
    def inference_crop_box_list(self, value: List[InferenceCropBox] = []):
        self._element_buffer._crop_count = len(value)
        self._element_buffer._inf_crop = (InfCropBoxBuffer * Const.MAX_CROP_BOX.value)(*[
            InfCropBoxBuffer(crop_box_index=inference_crop_box.crop_box_index,
                             x=inference_crop_box.x,
                             y=inference_crop_box.y,
                             width=inference_crop_box.width,
                             height=inference_crop_box.height) for inference_crop_box in value])

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'image': self.image.copy(),
            'width': self.width,
            'height': self.height,
            'image_format': str(self.image_format),
            'resize_mode': str(self.resize_mode),
            'padding_mode': str(self.padding_mode),
            'normalize_mode': str(self.normalize_mode),
            'crop_count': str(self.crop_count),
            'inference_crop_box_list': {}
        }

        for idx, inference_crop_box_element in enumerate(self.inference_crop_box_list):
            member_variable_dict['inference_crop_box_list'][idx] = inference_crop_box_element.get_member_variable_dict()

        return member_variable_dict


class GenericImageInferenceDescriptor(ValueBase, ValueRepresentBase):
    """
    Multiple input inference descriptor for images.

    Attributes
    ----------
    model_id : int, default=0
        Target inference model ID.
    inference_number : int, default=0
        Inference sequence number.
    input_node_image_list : List[GenericInputNodeImage], default=[]
        Multiple input inference image data descriptors (The image data order must be mapping model input tensor order as shown in ModelNefDescriptor).
    """

    def __init__(self,
                 inference_number: int = 0,
                 model_id: int = 0,
                 input_node_image_list: List[GenericInputNodeImage] = []):
        """
        Multiple input inference descriptor for images.

        Attributes
        ----------
        model_id : int, default=0
            Target inference model ID.
        inference_number : int, default=0
            Inference sequence number.
        input_node_image_list : List[GenericInputNodeImage], default=[]
            Multiple input inference image data descriptors (Max number of input image is 5) (The image data order must be mapping model input tensor order as shown in ModelNefDescriptor).
        """
        ValueBase.__init__(self,
                           element_buffer_class=GenericImageInferenceDescriptorBuffer,
                           inference_number=inference_number,
                           model_id=model_id,
                           generic_input_node_image_buffer_list=[
                               GenericInputNodeImageBuffer(
                                   width=input_node_image.width,
                                   height=input_node_image.height,
                                   resize_mode=input_node_image.resize_mode,
                                   padding_mode=input_node_image.padding_mode,
                                   image_format=input_node_image.image_format,
                                   normalize_mode=input_node_image.normalize_mode,
                                   inference_crop_box_buffer_list=[
                                       InfCropBoxBuffer(
                                           crop_box_index=inference_crop_box.crop_box_index,
                                           x=inference_crop_box.x,
                                           y=inference_crop_box.y,
                                           width=inference_crop_box.width,
                                           height=inference_crop_box.height
                                       ) for inference_crop_box in input_node_image.inference_crop_box_list
                                   ],
                                   image_buffer=input_node_image.image.tobytes()
                               ) for input_node_image in input_node_image_list
                           ])

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def model_id(self) -> int:
        """
        int: Target inference model ID.
        """
        return self._element_buffer._model_id

    @model_id.setter
    def model_id(self, value: int):
        self._element_buffer._model_id = value

    @property
    def inference_number(self) -> int:
        """
        int: Inference sequence number.
        """
        return self._element_buffer._inference_number

    @inference_number.setter
    def inference_number(self, value: int):
        self._element_buffer._inference_number = value

    @property
    def input_node_image_num(self) -> int:
        """
        int: Number of multiple input inference image data descriptors in input_node_image_list.
        """
        return self._element_buffer._num_input_node_image

    @property
    def input_node_image_list(self) -> List[GenericInputNodeImage]:
        """
        List[kp.GenericInputNodeImage]: Multiple input inference image data descriptors (Max number of input image is 5) (The image data order must be mapping model input tensor order as shown in ModelNefDescriptor).
        """
        generic_input_node_image_list = [GenericInputNodeImage() for _ in range(self.input_node_image_num)]
        generic_input_node_image_buffer_list = list(self._element_buffer._input_node_image_list)[
                                               :self.input_node_image_num]

        for idx, generic_input_node_image_buffer in enumerate(generic_input_node_image_buffer_list):
            generic_input_node_image_list[idx]._element_buffer = generic_input_node_image_buffer

        return generic_input_node_image_list

    @input_node_image_list.setter
    def input_node_image_list(self, value: List[GenericInputNodeImage]):
        if Const.MAX_INPUT_NODE_COUNT.value < len(value):
            _check_api_return_code(
                result='invalid input image number {} for multiple input inferece, the maximum number of input images is {}.'.format(
                    len(value), Const.MAX_INPUT_NODE_COUNT.value),
                api_return_code=ApiReturnCode(ApiReturnCode.KP_ERROR_INVALID_PARAM_12))

        self._element_buffer._num_input_node_image = len(value)
        self._element_buffer._input_node_image_list = (GenericInputNodeImageBuffer * Const.MAX_INPUT_NODE_COUNT.value)(
            *[
                GenericInputNodeImageBuffer(
                    width=input_node_image.width,
                    height=input_node_image.height,
                    resize_mode=input_node_image.resize_mode,
                    padding_mode=input_node_image.padding_mode,
                    image_format=input_node_image.image_format,
                    normalize_mode=input_node_image.normalize_mode,
                    inference_crop_box_buffer_list=[
                        InfCropBoxBuffer(
                            crop_box_index=inference_crop_box.crop_box_index,
                            x=inference_crop_box.x,
                            y=inference_crop_box.y,
                            width=inference_crop_box.width,
                            height=inference_crop_box.height) for inference_crop_box in
                        input_node_image.inference_crop_box_list],
                    image_buffer=input_node_image.image.tobytes()
                ) for input_node_image in value
            ])

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'model_id': self.model_id,
            'inference_number': self.inference_number,
            'input_node_image_num': self.input_node_image_num,
            'input_node_image_list': {}
        }

        for idx, input_node_image in enumerate(self.input_node_image_list):
            member_variable_dict['input_node_image_list'][idx] = input_node_image.get_member_variable_dict()

        return member_variable_dict


class GenericImageInferenceResultHeader(ValueBase, ValueRepresentBase):
    """
    Multiple input image inference raw output descriptor.

    Attributes
    ----------
    inference_number : int, default=0
        Inference sequence number.
    crop_number : int, default=0
        Crop box sequence number.
    num_output_node : int, default=0
        Total number of output nodes.
    product_id : int, default=0
        USB PID (Product ID).
    hw_pre_proc_info_list : List[kp.HwPreProcInfo], default=[]
        Hardware pre-process information for each input node.
    """

    def __init__(self,
                 inference_number: int = 0,
                 crop_number: int = 0,
                 num_output_node: int = 0,
                 product_id: int = 0,
                 hw_pre_proc_info_list: List[HwPreProcInfo] = []):
        """
        Multiple input image inference raw output descriptor.

        Attributes
        ----------
        inference_number : int, default=0
            Inference sequence number.
        crop_number : int, default=0
            Crop box sequence number.
        num_output_node : int, default=0
            Total number of output nodes.
        product_id : int, default=0
            USB PID (Product ID).
        hw_pre_proc_info_list : List[kp.HwPreProcInfo], default=[]
            Hardware pre-process information for each input node.
        """
        ValueBase.__init__(self,
                           element_buffer_class=GenericImageInferenceResultHeaderBuffer,
                           inference_number=inference_number,
                           crop_number=crop_number,
                           num_output_node=num_output_node,
                           product_id=product_id,
                           hw_pre_proc_info_list=[
                               hw_pre_proc_info._get_element_buffer() for hw_pre_proc_info in hw_pre_proc_info_list
                           ])

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def inference_number(self) -> int:
        """
        int: Inference sequence number.
        """
        return self._element_buffer._inference_number

    @property
    def crop_number(self) -> int:
        """
        int: Crop box sequence number.
        """
        return self._element_buffer._crop_number

    @property
    def num_output_node(self) -> int:
        """
        int: Total number of output nodes.
        """
        return self._element_buffer._num_output_node

    @property
    def product_id(self) -> int:
        """
        int: USB PID (Product ID).
        """
        return self._element_buffer._product_id

    @property
    def num_hw_pre_proc_info(self) -> int:
        """
        int: Number of hardware pre-process information.
        """
        return self._element_buffer._num_pre_proc_info

    @property
    def hw_pre_proc_info_list(self) -> List[HwPreProcInfo]:
        """
        List[kp.HwPreProcInfo]: Hardware pre-process information for each input node.
        """
        hw_pre_proc_info_list = [HwPreProcInfo() for _ in range(self.num_hw_pre_proc_info)]
        hw_pre_proc_info_buffer_list = list(self._element_buffer._hw_pre_proc_info_list)[:self.num_hw_pre_proc_info]

        for idx, hw_pre_proc_info_buffer in enumerate(hw_pre_proc_info_buffer_list):
            hw_pre_proc_info_list[idx]._element_buffer = hw_pre_proc_info_buffer

        return hw_pre_proc_info_list

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'inference_number': self.inference_number,
            'crop_number': self.crop_number,
            'num_output_node': self.num_output_node,
            'product_id': self.product_id,
            'num_hw_pre_proc_info': self.num_hw_pre_proc_info,
            'hw_pre_proc_info_list': {}
        }

        for idx, hw_pre_proc_info in enumerate(self.hw_pre_proc_info_list):
            member_variable_dict['hw_pre_proc_info_list'][idx] = hw_pre_proc_info.get_member_variable_dict()

        return member_variable_dict


class GenericImageInferenceResult(ValueRepresentBase):
    """
    Generic multiple input inference raw result.

    Attributes
    ----------
    header : kp.GenericImageInferenceResultHeader
        Multiple input image inference raw output descriptor.
    raw_result : kp.GenericRawResultNDArray
        Inference raw result buffer.
    """

    def __init__(self, buffer_size: int):
        """
        Generic multiple input inference raw result.

        Attributes
        ----------
        header : kp.GenericImageInferenceResultHeader
            Multiple input image inference raw output descriptor.
        raw_result : kp.GenericRawResultNDArray
            Inference raw result buffer.
        """
        self.__header = GenericImageInferenceResultHeader()
        self.__raw_result = GenericRawResultNDArray(buffer_size=buffer_size)

    @property
    def header(self) -> GenericImageInferenceResultHeader:
        """
        kp.GenericImageInferenceResultHeader: Multiple input image inference raw output descriptor.
        """
        return self.__header

    @property
    def raw_result(self) -> GenericRawResultNDArray:
        """
        kp.GenericRawResultNDArray: Inference raw result buffer.
        """
        return self.__raw_result

    def get_member_variable_dict(self) -> dict:
        return {
            'header': self.header.get_member_variable_dict(),
            'raw_result': self.raw_result.get_member_variable_dict()
        }


class GenericInputNodeData(ValueBase, ValueRepresentBase):
    """
    Single data descriptor for bypass pre-processing inference.

    Attributes
    ----------
    buffer : bytes, default=bytes()
        The data bytes contains the inference data.
    """

    def __init__(self,
                 buffer: bytes = bytes()):
        """
        Single data descriptor for bypass pre-processing inference.

        Attributes
        ----------
        buffer : bytes, default=bytes()
            The data bytes contains the inference data.
        """
        ValueBase.__init__(self,
                           element_buffer_class=GenericInputNodeDataBuffer,
                           buffer=buffer)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def buffer(self) -> bytes:
        """
        bytes: The data bytes contains the inference data.
        """
        return ctypes.string_at(self._element_buffer._buffer, self.buffer_size)

    @buffer.setter
    def buffer(self, value: bytes):
        self._element_buffer._buffer_size = len(value)
        self._element_buffer._buffer = (ctypes.c_ubyte * self._element_buffer._buffer_size).from_buffer(
            bytearray(value))

    @property
    def buffer_size(self) -> int:
        """
        int: Inference data buffer size.
        """
        return self._element_buffer._buffer_size

    def get_member_variable_dict(self) -> dict:
        return {
            'buffer': str(self.buffer),
            'buffer_size': self.buffer_size,
        }


class GenericDataInferenceDescriptor(ValueBase, ValueRepresentBase):
    """
    Multiple input inference descriptor for bypass pre-processing inference.

    Attributes
    ----------
    model_id : int, default=0
        Target inference model ID.
    inference_number : int, default=0
        Inference sequence number.
    input_node_data_list : List[GenericInputNodeData], default=[]
        Multiple input inference data descriptors (The data order must be mapping model input tensor order as shown in ModelNefDescriptor).
    """

    def __init__(self,
                 inference_number: int = 0,
                 model_id: int = 0,
                 input_node_data_list: List[GenericInputNodeData] = []):
        """
        Multiple input inference descriptor for bypass pre-processing inference.

        Attributes
        ----------
        model_id : int, default=0
            Target inference model ID.
        inference_number : int, default=0
            Inference sequence number.
        input_node_data_list : List[GenericInputNodeData], default=[]
            Multiple input inference data descriptors (The data order must be mapping model input tensor order as shown in ModelNefDescriptor).
        """

        if Const.MAX_INPUT_NODE_COUNT.value < len(input_node_data_list):
            _check_api_return_code(
                result='invalid input image number {} for multiple input inferece, the maximum number of input images is {}.'.format(
                    len(input_node_data_list), Const.MAX_INPUT_NODE_COUNT.value),
                api_return_code=ApiReturnCode(ApiReturnCode.KP_ERROR_INVALID_PARAM_12))

        ValueBase.__init__(self,
                           element_buffer_class=GenericDataInferenceImageHeaderBuffer,
                           inference_number=inference_number,
                           model_id=model_id,
                           generic_input_node_data_buffer_list=[
                               GenericInputNodeDataBuffer(buffer=input_node_data.buffer)
                               for input_node_data in input_node_data_list
                           ])

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def model_id(self) -> int:
        """
        int: Target inference model ID.
        """
        return self._element_buffer._model_id

    @model_id.setter
    def model_id(self, value: int):
        self._element_buffer._model_id = value

    @property
    def inference_number(self) -> int:
        """
        int: Inference sequence number.
        """
        return self._element_buffer._inference_number

    @inference_number.setter
    def inference_number(self, value: int):
        self._element_buffer._inference_number = value

    @property
    def input_node_data_num(self) -> int:
        """
        int: Number of multiple input inference data descriptors in input_node_data_list.
        """
        return self._element_buffer._num_input_node_data

    @property
    def input_node_data_list(self) -> List[GenericInputNodeData]:
        """
        List[GenericInputNodeData]: Multiple input inference data descriptors (The data order must be mapping model input tensor order as shown in ModelNefDescriptor).
        """
        generic_raw_one_image_bypass_pre_proc_list = [GenericInputNodeData() for _ in
                                                      range(self.input_node_data_num)]
        generic_input_node_data_buffer_list = list(self._element_buffer._input_node_data_list)[
                                              :self.input_node_data_num]

        for idx, generic_input_node_data_buffer in enumerate(generic_input_node_data_buffer_list):
            generic_raw_one_image_bypass_pre_proc_list[idx]._element_buffer = generic_input_node_data_buffer

        return generic_raw_one_image_bypass_pre_proc_list

    @input_node_data_list.setter
    def input_node_data_list(self, value: List[GenericInputNodeData]):
        if Const.MAX_INPUT_NODE_COUNT.value < len(value):
            _check_api_return_code(
                result='invalid input image number {} for multiple input inferece, the maximum number of input images is {}.'.format(
                    len(value), Const.MAX_INPUT_NODE_COUNT.value),
                api_return_code=ApiReturnCode(ApiReturnCode.KP_ERROR_INVALID_PARAM_12))

        self._element_buffer._num_input_node_data = len(value)
        self._element_buffer._input_node_data_list = (
                GenericInputNodeDataBuffer * Const.MAX_INPUT_NODE_COUNT.value)(
            *[GenericInputNodeDataBuffer(buffer=input_node_data.buffer) for input_node_data in value]
        )

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'model_id': self.model_id,
            'inference_number': self.inference_number,
            'input_node_data_num': self.input_node_data_num,
            'input_node_data_list': {}
        }

        for idx, input_node_data in enumerate(self.input_node_data_list):
            member_variable_dict['input_node_data_list'][idx] = input_node_data.get_member_variable_dict()

        return member_variable_dict


class GenericDataInferenceResultHeader(ValueBase, ValueRepresentBase):
    """
    Multiple input bypass pre-processing inference raw output descriptor.

    Attributes
    ----------
    inference_number : int, default=0
        Inference sequence number.
    crop_number : int, default=0
        Crop box sequence number.
    num_output_node : int, default=0
        Total number of output nodes.
    product_id : int, default=0
        USB PID (Product ID).
    """

    def __init__(self,
                 inference_number: int = 0,
                 crop_number: int = 0,
                 num_output_node: int = 0,
                 product_id: int = 0):
        """
        Multiple input bypass pre-processing inference raw output descriptor.

        Attributes
        ----------
        inference_number : int, default=0
            Inference sequence number.
        crop_number : int, default=0
            Crop box sequence number.
        num_output_node : int, default=0
            Total number of output nodes.
        product_id : int, default=0
            USB PID (Product ID).
        """
        ValueBase.__init__(self,
                           element_buffer_class=GenericDataInferenceResultHeaderBuffer,
                           inference_number=inference_number,
                           crop_number=crop_number,
                           num_output_node=num_output_node,
                           product_id=product_id)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def inference_number(self) -> int:
        """
        int: Inference sequence number.
        """
        return self._element_buffer._inference_number

    @property
    def crop_number(self) -> int:
        """
        int: Crop box sequence number.
        """
        return self._element_buffer._crop_number

    @property
    def num_output_node(self) -> int:
        """
        int: Total number of output nodes.
        """
        return self._element_buffer._num_output_node

    @property
    def product_id(self) -> int:
        """
        int: USB PID (Product ID).
        """
        return self._element_buffer._product_id

    def get_member_variable_dict(self) -> dict:
        return {
            'inference_number': self.inference_number,
            'crop_number': self.crop_number,
            'num_output_node': self.num_output_node,
            'product_id': self.product_id
        }


class GenericDataInferenceResult(ValueRepresentBase):
    """
    Multiple input bypass pre-processing inference raw result.

    Attributes
    ----------
    header : kp.GenericDataInferenceResultHeader
        Multiple input bypass pre-processing inference raw output descriptor.
    raw_result : kp.GenericRawResultNDArray
        Inference raw result buffer.
    """

    def __init__(self, buffer_size: int):
        """
        Multiple input bypass pre-processing inference raw result.

        Attributes
        ----------
        header : kp.GenericDataInferenceResultHeader
            Multiple input bypass pre-processing inference raw output descriptor.
        raw_result : kp.GenericRawResultNDArray
            Inference raw result buffer.
        """
        self.__header = GenericDataInferenceResultHeader()
        self.__raw_result = GenericRawResultNDArray(buffer_size=buffer_size)

    @property
    def header(self) -> GenericDataInferenceResultHeader:
        """
        kp.GenericDataInferenceResultHeader: Multiple input bypass pre-processing inference raw output descriptor.
        """
        return self.__header

    @property
    def raw_result(self) -> GenericRawResultNDArray:
        """
        kp.GenericRawResultNDArray: Inference raw result buffer.
        """
        return self.__raw_result

    def get_member_variable_dict(self) -> dict:
        return {
            'header': self.header.get_member_variable_dict(),
            'raw_result': self.raw_result.get_member_variable_dict()
        }


class InferenceFloatNodeOutput(ValueBase, ValueRepresentBase):
    """
    Generic inference node output in floating-point format.

    Attributes
    ----------
    width : int, default=0
        Width of output node.
    height : int, default=0
        Height of output node.
    channel : int, default=0
        Channel of output node.
    num_data : int, default=0
        Total number of floating-point values.
    data : np.ndarray, default=np.array([])
        N-dimensional numpy.ndarray of feature map. (Channel ordering: KL520 - H,C,W; KL720 - C,H,W)
    channels_ordering : kp.ChannelOrdering, default=kp.ChannelOrdering.KP_CHANNEL_ORDERING_CHW
        Specify channel ordering of feature map. (Options: KP_CHANNEL_ORDERING_HCW, KP_CHANNEL_ORDERING_CHW)
    """

    def __init__(self,
                 width: int = 0,
                 height: int = 0,
                 channel: int = 0,
                 num_data: int = 0,
                 data: np.ndarray = np.array([]),
                 channels_ordering: ChannelOrdering = ChannelOrdering.KP_CHANNEL_ORDERING_CHW):
        """
        Generic inference node output in floating-point format.

        Parameters
        ----------
        width : int, default=0
            Width of output node.
        height : int, default=0
            Height of output node.
        channel : int, default=0
            Channel of output node.
        num_data : int, default=0
            Total number of floating-point values.
        data : np.ndarray, default=np.array([])
            N-dimensional numpy.ndarray of feature map. (Channel ordering: KL520 - H,C,W; KL720 - C,H,W)
        channels_ordering : kp.ChannelOrdering, default=kp.ChannelOrdering.KP_CHANNEL_ORDERING_CHW
            Specify channel ordering of feature map. (Options: KP_CHANNEL_ORDERING_HCW, KP_CHANNEL_ORDERING_CHW)
        """
        ValueBase.__init__(self,
                           element_buffer_class=InfFloatNodeOutputBuffer,
                           width=width,
                           height=height,
                           channel=channel,
                           num_data=num_data,
                           data=data)

        self.__channels_ordering = channels_ordering

    def __del__(self):
        if self._is_allocate_from_c:
            wrapper_utils.c_free(pointer=self._element_buffer.contents)
            self._is_allocate_from_c = False

    def _cast_element_buffer(self) -> None:
        self._element_buffer = ctypes.pointer(self._element_buffer)

    def _get_element_buffer(self) -> Union[None, ctypes.POINTER]:
        return self._element_buffer

    @property
    def width(self) -> int:
        """
        int: Width of output node.
        """
        return self._element_buffer.contents._width

    @property
    def height(self) -> int:
        """
        int: Height of output node.
        """
        return self._element_buffer.contents._height

    @property
    def channel(self) -> int:
        """
        int: Channel of output node.
        """
        return self._element_buffer.contents._channel

    @property
    def num_data(self) -> int:
        """
        int: Total number of floating-point values.
        """
        return self._element_buffer.contents._num_data

    @property
    def ndarray(self) -> np.ndarray:
        """
        numpy.ndarray: N-dimensional numpy.ndarray of feature map.
        """
        if self._is_allocate_from_c:
            address = ctypes.addressof(self._element_buffer.contents._data)
            buffer_as_ctypes_array = (ctypes.c_float * self._element_buffer.contents._num_data).from_address(address)
        else:
            buffer_as_ctypes_array = self._element_buffer.contents._data[:self._element_buffer.contents._num_data]

        ret_ndarray = np.ctypeslib.as_array(buffer_as_ctypes_array)

        if self.channels_ordering == ChannelOrdering.KP_CHANNEL_ORDERING_CHW:
            ret_ndarray = ret_ndarray.reshape((self.channel, self.height, self.width))
        elif self.channels_ordering == ChannelOrdering.KP_CHANNEL_ORDERING_HCW:
            ret_ndarray = ret_ndarray.reshape((self.height, self.channel, self.width))
        elif self.channels_ordering == ChannelOrdering.KP_CHANNEL_ORDERING_HWC:
            ret_ndarray = ret_ndarray.reshape((self.height, self.width, self.channel))

        ret_ndarray = np.expand_dims(ret_ndarray, axis=0)

        return ret_ndarray.copy()

    @property
    def channels_ordering(self) -> ChannelOrdering:
        """
        kp.ChannelOrdering: Channel ordering of feature map. (Options: KP_CHANNEL_ORDERING_HCW, KP_CHANNEL_ORDERING_CHW)
        """
        return self.__channels_ordering

    def get_member_variable_dict(self) -> dict:
        return {
            'width': self.width,
            'height': self.height,
            'channel': self.channel,
            'channels_ordering': str(self.channels_ordering),
            'num_data': self.num_data,
            'ndarray': self.ndarray
        }


class InferenceFixedNodeOutput(ValueBase, ValueRepresentBase):
    """
    Generic inference node output in fixed-point format.

    Attributes
    ----------
    width : int, default=0
        Width of output node.
    height : int, default=0
        Height of output node.
    channel : int, default=0
        Channel of output node.
    radix : int, default=0
        Radix for fixed/floating point conversion.
    scale : float, default=0
        Scale for fixed/floating point conversion.
    factor : float, default=0
        Conversion factor for fixed-point to floating-point conversion - formulation: scale * (2 ^ radix).
    dtype : FixedPointDType, default=FixedPointDType.
            fixed-point data type.
    num_data : int, default=0
        Total number of fixed-point values.
    data : np.ndarray, default=np.array([])
        N-dimensional numpy.ndarray of feature map. (Channel ordering: KL520 - H,C,W; KL720 - C,H,W)
    channels_ordering : kp.ChannelOrdering, default=kp.ChannelOrdering.KP_CHANNEL_ORDERING_CHW
        Specify channel ordering of feature map. (Options: KP_CHANNEL_ORDERING_HCW, KP_CHANNEL_ORDERING_CHW)
    """

    def __init__(self,
                 width: int = 0,
                 height: int = 0,
                 channel: int = 0,
                 radix: int = 0,
                 scale: float = 0,
                 factor: float = 0,
                 dtype: FixedPointDType = FixedPointDType.KP_FIXED_POINT_DTYPE_UNKNOWN,
                 num_data: int = 0,
                 data: np.ndarray = np.array([]),
                 channels_ordering: ChannelOrdering = ChannelOrdering.KP_CHANNEL_ORDERING_CHW):
        """
        Generic inference node output in fixed-point format.

        Parameters
        ----------
        width : int, default=0
            Width of output node.
        height : int, default=0
            Height of output node.
        channel : int, default=0
            Channel of output node.
        radix : int, default=0
            Radix for fixed/floating point conversion.
        scale : float, default=0
            Scale for fixed/floating point conversion.
        factor : float, default=0
            Conversion factor for fixed-point to floating-point conversion - formulation: scale * (2 ^ radix).
        dtype : FixedPointDType, default=FixedPointDType.
            fixed-point data type.
        num_data : int, default=0
            Total number of fixed-point values.
        data : np.ndarray, default=np.array([])
            N-dimensional numpy.ndarray of feature map. (Channel ordering: KL520 - H,C,W; KL720 - C,H,W)
        channels_ordering : kp.ChannelOrdering, default=kp.ChannelOrdering.KP_CHANNEL_ORDERING_CHW
            Specify channel ordering of feature map. (Options: KP_CHANNEL_ORDERING_HCW, KP_CHANNEL_ORDERING_CHW)
        """
        ValueBase.__init__(self,
                           element_buffer_class=InfFixedNodeOutputBuffer,
                           width=width,
                           height=height,
                           channel=channel,
                           radix=radix,
                           scale=scale,
                           factor=factor,
                           dtype=dtype,
                           num_data=num_data,
                           data=data)

        self.__channels_ordering = channels_ordering

    def __del__(self):
        if self._is_allocate_from_c:
            wrapper_utils.c_free(pointer=self._element_buffer.contents)
            self._is_allocate_from_c = False

    def _cast_element_buffer(self) -> None:
        self._element_buffer = ctypes.pointer(self._element_buffer)

    def _get_element_buffer(self) -> Union[None, ctypes.POINTER]:
        return self._element_buffer

    @property
    def width(self) -> int:
        """
        int: Width of output node.
        """
        return self._element_buffer.contents._width

    @property
    def height(self) -> int:
        """
        int: Height of output node.
        """
        return self._element_buffer.contents._height

    @property
    def channel(self) -> int:
        """
        int: Channel of output node.
        """
        return self._element_buffer.contents._channel

    @property
    def radix(self) -> int:
        """
        int: Radix for fixed/floating point conversion.
        """
        return self._element_buffer.contents._radix

    @property
    def scale(self) -> float:
        """
        float: Scale for fixed/floating point conversion.
        """
        return self._element_buffer.contents._scale

    @property
    def factor(self) -> float:
        """
        float: Conversion factor for fixed-point to floating-point conversion - formulation: scale * (2 ^ radix).
        """
        return self._element_buffer.contents._factor

    @property
    def dtype(self) -> FixedPointDType:
        """
        FixedPointDType: fixed-point data type.
        """
        return FixedPointDType(self._element_buffer.contents._fixed_point_dtype)

    @property
    def num_data(self) -> int:
        """
        int: Total number of fixed-point values.
        """
        return self._element_buffer.contents._num_data

    @property
    def ndarray(self) -> np.ndarray:
        """
        numpy.ndarray: N-dimensional numpy.ndarray of feature map.
        """
        if self._is_allocate_from_c:
            address = ctypes.addressof(self._element_buffer.contents._data)

            if FixedPointDType.KP_FIXED_POINT_DTYPE_INT8 == self.dtype:
                ctypes_dtype = ctypes.c_int8
            elif FixedPointDType.KP_FIXED_POINT_DTYPE_INT16 == self.dtype:
                ctypes_dtype = ctypes.c_int16
            else:
                raise AttributeError('Unknown fixed-point data type {}'.format(self.dtype.value))

            buffer_as_ctypes_array = (ctypes_dtype * self._element_buffer.contents._num_data).from_address(address)
        else:
            buffer_as_ctypes_array = self._element_buffer.contents._data[:self._element_buffer.contents._num_data]

        ret_ndarray = np.ctypeslib.as_array(buffer_as_ctypes_array)

        if self.channels_ordering == ChannelOrdering.KP_CHANNEL_ORDERING_CHW:
            ret_ndarray = ret_ndarray.reshape((self.channel, self.height, self.width))
        elif self.channels_ordering == ChannelOrdering.KP_CHANNEL_ORDERING_HCW:
            ret_ndarray = ret_ndarray.reshape((self.height, self.channel, self.width))
        elif self.channels_ordering == ChannelOrdering.KP_CHANNEL_ORDERING_HWC:
            ret_ndarray = ret_ndarray.reshape((self.height, self.width, self.channel))

        ret_ndarray = np.expand_dims(ret_ndarray, axis=0)

        return ret_ndarray.copy()

    @property
    def channels_ordering(self) -> ChannelOrdering:
        """
        kp.ChannelOrdering: Channel ordering of feature map. (Options: KP_CHANNEL_ORDERING_HCW, KP_CHANNEL_ORDERING_CHW)
        """
        return self.__channels_ordering

    def get_member_variable_dict(self) -> dict:
        return {
            'width': self.width,
            'height': self.height,
            'channel': self.channel,
            'radix': self.radix,
            'scale': self.scale,
            'factor': self.factor,
            'channels_ordering': str(self.channels_ordering),
            'dtype': str(self.dtype),
            'num_data': self.num_data,
            'ndarray': self.ndarray
        }

    def to_float_node_output(self) -> InferenceFloatNodeOutput:
        """
        Convert fixed-point node output to floating-point node output.

        Returns
        -------
        inference_float_node_output : kp.InferenceFloatNodeOutput
        """
        return InferenceFloatNodeOutput(
            width=self.width,
            height=self.height,
            channel=self.channel,
            num_data=self.num_data,
            data=(self.ndarray / self.factor).astype(np.float32),
            channels_ordering=self.channels_ordering
        )


class ProfileModelStatistics(ValueBase, ValueRepresentBase):
    """
    One model inference statistic data.

    Attributes
    ----------
    model_id : int, default=0
        Target inference model ID.
    inference_count : int, default=0
        Number of Inference in the statistic.
    cpu_op_count : int, default=0
        Number of CPU operation per inference.
    avg_pre_process_ms : float, default=0
        Average pre-process time in milliseconds.
    avg_inference_ms : float, default=0
        Average inference time in milliseconds.
    avg_cpu_op_ms : float, default=0
        Average CPU operation time per-inference in milliseconds.
    avg_cpu_op_per_cpu_node_ms : float, default=0
        Average CPU operation time per-CPU node in milliseconds.
    avg_post_process_ms : float, default=0
        Average post-process time in milliseconds.
    """

    def __init__(self,
                 model_id: int = 0,
                 inference_count: int = 0,
                 cpu_op_count: int = 0,
                 avg_pre_process_ms: float = 0,
                 avg_inference_ms: float = 0,
                 avg_cpu_op_ms: float = 0,
                 avg_cpu_op_per_cpu_node_ms: float = 0,
                 avg_post_process_ms: float = 0):
        """
        One model inference statistic data.

        Parameters
        ----------
        model_id : int, default=0
            Target inference model ID.
        inference_count : int, default=0
            Number of Inference in the statistic.
        cpu_op_count : int, default=0
            Number of CPU operation per inference.
        avg_pre_process_ms : float, default=0
            Average pre-process time in milliseconds.
        avg_inference_ms : float, default=0
            Average inference time in milliseconds.
        avg_cpu_op_ms : float, default=0
            Average CPU operation time per-inference in milliseconds.
        avg_cpu_op_per_cpu_node_ms : float, default=0
            Average CPU operation time per-CPU node in milliseconds.
        avg_post_process_ms : float, default=0
            Average post-process time in milliseconds.
        """
        ValueBase.__init__(self,
                           element_buffer_class=ProfileModelStatisticsBuffer,
                           model_id=model_id,
                           inference_count=inference_count,
                           cpu_op_count=cpu_op_count,
                           avg_pre_process_ms=avg_pre_process_ms,
                           avg_inference_ms=avg_inference_ms,
                           avg_cpu_op_ms=avg_cpu_op_ms,
                           avg_cpu_op_per_cpu_node_ms=avg_cpu_op_per_cpu_node_ms,
                           avg_post_process_ms=avg_post_process_ms)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def model_id(self) -> int:
        """
        int: Target inference model ID.
        """
        return self._element_buffer._model_id

    @property
    def inference_count(self) -> int:
        """
        int: Number of Inference in the statistic.
        """
        return self._element_buffer._inf_count

    @property
    def cpu_op_count(self) -> int:
        """
        int: Number of CPU operation per inference.
        """
        return self._element_buffer._cpu_op_count

    @property
    def avg_pre_process_ms(self) -> float:
        """
        float: Average pre-process time in milliseconds.
        """
        return self._element_buffer._avg_pre_process_ms

    @property
    def avg_inference_ms(self) -> float:
        """
        float: Average inference time in milliseconds.
        """
        return self._element_buffer._avg_inference_ms

    @property
    def avg_cpu_op_ms(self) -> float:
        """
        float: Average CPU operation time per-inference in milliseconds.
        """
        return self._element_buffer._avg_cpu_op_ms

    @property
    def avg_cpu_op_per_cpu_node_ms(self) -> float:
        """
        float: Average CPU operation time per-CPU node in milliseconds.
        """
        return self._element_buffer._avg_cpu_op_per_cpu_node_ms

    @property
    def avg_post_process_ms(self) -> float:
        """
        float: Average post-process time in milliseconds.
        """
        return self._element_buffer._avg_post_process_ms

    def get_member_variable_dict(self) -> dict:
        return {
            'model_id': self.model_id,
            'inference_count': self.inference_count,
            'cpu_op_count': self.cpu_op_count,
            'avg_pre_process_ms': self.avg_pre_process_ms,
            'avg_inference_ms': self.avg_inference_ms,
            'avg_cpu_op_ms': self.avg_cpu_op_ms,
            'avg_cpu_op_per_cpu_node_ms': self.avg_cpu_op_per_cpu_node_ms,
            'avg_post_process_ms': self.avg_post_process_ms
        }


class ProfileData(ValueBase, ValueRepresentBase):
    """
    Model inference profiling data.

    Attributes
    ----------
    model_profiled_num : int, default=0
        Number of profiled model.
    model_statistic_list : List[kp.ProfileModelStatistics], default=[]
        List of model inference statistic data.
    """

    def __init__(self,
                 model_statistic_list: List[ProfileModelStatistics] = []):
        """
        Generic inference node output in fixed-point format.

        Parameters
        ----------
        model_statistic_list : List[kp.ProfileModelStatistics], default=[]
            List of model inference statistic data.
        """
        ValueBase.__init__(self,
                           element_buffer_class=ProfileDataBuffer,
                           model_statistics_buffer_list=[model_statistic._element_buffer for model_statistic in model_statistic_list])

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def model_profiled_num(self) -> int:
        """
        int: Number of profiled model.
        """
        return self._element_buffer._num_model_profiled

    @property
    def model_statistic_list(self) -> List[ProfileModelStatistics]:
        """
        List[kp.ProfileModelStatistics]: List of model inference statistic data.
        """
        return [
            ProfileModelStatistics(
                model_id=model_statistic_buff._model_id,
                inference_count=model_statistic_buff._inf_count,
                cpu_op_count=model_statistic_buff._cpu_op_count,
                avg_pre_process_ms=model_statistic_buff._avg_pre_process_ms,
                avg_inference_ms=model_statistic_buff._avg_inference_ms,
                avg_cpu_op_ms=model_statistic_buff._avg_cpu_op_ms,
                avg_cpu_op_per_cpu_node_ms=model_statistic_buff._avg_cpu_op_per_cpu_node_ms,
                avg_post_process_ms=model_statistic_buff._avg_post_process_ms
            ) for model_statistic_buff in self._element_buffer._model_st[:self._element_buffer._num_model_profiled]
        ]

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'model_profiled_num': self.model_profiled_num,
            'model_statistic_list': {}
        }

        for idx, model_statistic_element in enumerate(self.model_statistic_list):
            member_variable_dict['model_statistic_list'][idx] = model_statistic_element.get_member_variable_dict()

        return member_variable_dict


class NpuPerformanceMonitorStatistics(ValueBase, ValueRepresentBase):
    """
    One model inference performance monitor statistic data.

    Attributes
    ----------
    model_id : int, default=0
        Target inference model ID.
    npu_clock_rate : int, default=0
        NPU clock rate.
    f0 : int, default=0
        Value of performance monitor mode f0.
    f0_time : float, default=0
        float: time of performance monitor mode f0.
    f1 : int, default=0
        Value of performance monitor mode f1.
    f1_time : float, default=0
        float: time of performance monitor mode f1.
    f2 : int, default=0
        Value of performance monitor mode f2.
    f2_time : float, default=0
        float: time of performance monitor mode f2.
    f3 : int, default=0
        Value of performance monitor mode f3.
    f3_time : float, default=0
        float: time of performance monitor mode f3.
    f4 : int, default=0
        Value of performance monitor mode f4.
    f4_time : float, default=0
        float: time of performance monitor mode f4.
    f5 : int, default=0
        Value of performance monitor mode f5.
    f5_time : float, default=0
        float: time of performance monitor mode f5.
    f6 : int, default=0
        Value of performance monitor mode f6.
    f6_time : float, default=0
        float: time of performance monitor mode f6.
    f7 : int, default=0
        Value of performance monitor mode f7.
    f7_time : float, default=0
        float: time of performance monitor mode f7.
    """

    def __init__(self,
                 model_id: int = 0,
                 npu_clock_rate: int = 0,
                 f0: int = 0,
                 f1: int = 0,
                 f2: int = 0,
                 f3: int = 0,
                 f4: int = 0,
                 f5: int = 0,
                 f6: int = 0,
                 f7: int = 0):
        """
        One model inference statistic data.

        Parameters
        ----------
        model_id : int, default=0
            Target inference model ID.
        npu_clock_rate : int, default=0
            NPU clock rate.
        f0 : int, default=0
            Value of performance monitor mode f0.
        f0_time : float, default=0
            float: time of performance monitor mode f0.
        f1 : int, default=0
            Value of performance monitor mode f1.
        f1_time : float, default=0
            float: time of performance monitor mode f1.
        f2 : int, default=0
            Value of performance monitor mode f2.
        f2_time : float, default=0
            float: time of performance monitor mode f2.
        f3 : int, default=0
            Value of performance monitor mode f3.
        f3_time : float, default=0
            float: time of performance monitor mode f3.
        f4 : int, default=0
            Value of performance monitor mode f4.
        f4_time : float, default=0
            float: time of performance monitor mode f4.
        f5 : int, default=0
            Value of performance monitor mode f5.
        f5_time : float, default=0
            float: time of performance monitor mode f5.
        f6 : int, default=0
            Value of performance monitor mode f6.
        f6_time : float, default=0
            float: time of performance monitor mode f6.
        f7 : int, default=0
            Value of performance monitor mode f7.
        f7_time : float, default=0
            float: time of performance monitor mode f7.
        """
        self.__npu_clock_rate = npu_clock_rate

        ValueBase.__init__(self,
                           element_buffer_class=NpuPerformanceMonitorStatisticsBuffer,
                           model_id=model_id,
                           npu_clock_rate=npu_clock_rate,
                           f0=f0,
                           f1=f1,
                           f2=f2,
                           f3=f3,
                           f4=f4,
                           f5=f5,
                           f6=f6,
                           f7=f7)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def model_id(self) -> int:
        """
        int: Target inference model ID.
        """
        return self._element_buffer._model_id

    @property
    def npu_clock_rate(self) -> int:
        """
        int: NPU clock rate.
        """
        return self._element_buffer._npu_clock_rate

    @property
    def f0(self) -> int:
        """
        int: Value of performance monitor mode f0.
        """
        return self._element_buffer._f0

    @property
    def f0_time(self) -> float:
        """
        float: time of performance monitor mode f0.
        """
        return self.f0 / (self.npu_clock_rate / 1000)

    @property
    def f1(self) -> int:
        """
        int: Value of performance monitor mode f1.
        """
        return self._element_buffer._f1

    @property
    def f1_time(self) -> float:
        """
        float: time of performance monitor mode f1.
        """
        return self.f1 / (self.npu_clock_rate / 1000)

    @property
    def f2(self) -> int:
        """
        int: Value of performance monitor mode f2.
        """
        return self._element_buffer._f2

    @property
    def f2_time(self) -> float:
        """
        float: time of performance monitor mode f2.
        """
        return self.f2 / (self.npu_clock_rate / 1000)

    @property
    def f3(self) -> int:
        """
        int: Value of performance monitor mode f3.
        """
        return self._element_buffer._f3

    @property
    def f3_time(self) -> float:
        """
        float: time of performance monitor mode f3.
        """
        return self.f3 / (self.npu_clock_rate / 1000)

    @property
    def f4(self) -> int:
        """
        int: Value of performance monitor mode f4.
        """
        return self._element_buffer._f4

    @property
    def f4_time(self) -> float:
        """
        float: time of performance monitor mode f4.
        """
        return self.f4 / (self.npu_clock_rate / 1000)

    @property
    def f5(self) -> int:
        """
        int: Value of performance monitor mode f5.
        """
        return self._element_buffer._f5

    @property
    def f5_time(self) -> float:
        """
        float: time of performance monitor mode f5.
        """
        return self.f5 / (self.npu_clock_rate / 1000)

    @property
    def f6(self) -> int:
        """
        int: Value of performance monitor mode f6.
        """
        return self._element_buffer._f6

    @property
    def f6_time(self) -> float:
        """
        float: time of performance monitor mode f6.
        """
        return self.f6 / (self.npu_clock_rate / 1000)

    @property
    def f7(self) -> int:
        """
        int: Value of performance monitor mode f7.
        """
        return self._element_buffer._f7

    @property
    def f7_time(self) -> float:
        """
        float: time of performance monitor mode f7.
        """
        return self.f7 / (self.npu_clock_rate / 1000)

    def get_member_variable_dict(self) -> dict:
        return {
            'model_id': self.model_id,
            'clock_cycle': {
                'f0': self.f0,
                'f1': self.f1,
                'f2': self.f2,
                'f3': self.f3,
                'f4': self.f4,
                'f5': self.f5,
                'f6': self.f6,
                'f7': self.f7
            },
            'time': {
                'f0 (ms)': round(self.f0_time, 3),
                'f1 (ms)': round(self.f1_time, 3),
                'f2 (ms)': round(self.f2_time, 3),
                'f3 (ms)': round(self.f3_time, 3),
                'f4 (ms)': round(self.f4_time, 3),
                'f5 (ms)': round(self.f5_time, 3),
                'f6 (ms)': round(self.f6_time, 3),
                'f7 (ms)': round(self.f7_time, 3)
            }
        }


class PerformanceMonitorData(ValueBase, ValueRepresentBase):
    """
    Model inference performance monitor data.

    Attributes
    ----------
    model_profiled_num : int, default=0
        Number of profiled model.
    model_statistic_list : List[kp.NpuPerformanceMonitorStatistics], default=[]
        List of performance monitor statistic data.
    """

    def __init__(self,
                 npu_clock_rate: int = 0,
                 model_statistic_list: List[NpuPerformanceMonitorStatistics] = []):
        """
        Model inference performance monitor data.

        Attributes
        ----------
        model_profiled_num : int, default=0
            Number of profiled model.
        model_statistic_list : List[kp.NpuPerformanceMonitorStatistics], default=[]
            List of performance monitor statistic data.
        """
        ValueBase.__init__(self,
                           element_buffer_class=PerformanceMonitorDataBuffer,
                           model_statistics_buffer_list=[model_statistic._element_buffer for model_statistic in model_statistic_list])

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def model_profiled_num(self) -> int:
        """
        int: Number of profiled model.
        """
        return self._element_buffer._num_model_profiled

    @property
    def model_statistic_list(self) -> List[NpuPerformanceMonitorStatistics]:
        """
        List[kp.NpuPerformanceMonitorStatistics]: List of performance monitor statistic data.
        """
        return [
            NpuPerformanceMonitorStatistics(
                model_id=model_statistic_buff._model_id,
                npu_clock_rate=model_statistic_buff._npu_clock_rate,
                f0=model_statistic_buff._f0,
                f1=model_statistic_buff._f1,
                f2=model_statistic_buff._f2,
                f3=model_statistic_buff._f3,
                f4=model_statistic_buff._f4,
                f5=model_statistic_buff._f5,
                f6=model_statistic_buff._f6,
                f7=model_statistic_buff._f7
            ) for model_statistic_buff in self._element_buffer._model_st[:self._element_buffer._num_model_profiled]
        ]

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'model_profiled_num': self.model_profiled_num,
            'model_statistic_list': {}
        }

        for idx, model_statistic_element in enumerate(self.model_statistic_list):
            member_variable_dict['model_statistic_list'][idx] = model_statistic_element.get_member_variable_dict()

        return member_variable_dict


class BoundingBox(ValueBase, ValueRepresentBase):
    """
    Bounding box descriptor.

    Attributes
    ----------
    x1 : float, default=0
        X coordinate of bounding box top-left corner.
    y1 : float, default=0
        Y coordinate of bounding box top-left corner.
    x2 : float, default=0
        X coordinate of bounding box bottom-right corner.
    y2 : float, default=0
        Y coordinate of bounding box bottom-right corner.
    score : float, default=0
        Probability score.
    class_num : int, default=0
        Class # (of many) with highest probability.
    """

    def __init__(self,
                 x1: float = 0,
                 y1: float = 0,
                 x2: float = 0,
                 y2: float = 0,
                 score: float = 0,
                 class_num: int = 0):
        """
        Bounding box descriptor.

        Parameters
        ----------
        x1 : float, default=0
            X coordinate of bounding box top-left corner.
        y1 : float, default=0
            Y coordinate of bounding box top-left corner.
        x2 : float, default=0
            X coordinate of bounding box bottom-right corner.
        y2 : float, default=0
            Y coordinate of bounding box bottom-right corner.
        score : float, default=0
            Probability score.
        class_num : int, default=0
            Class # (of many) with highest probability.
        """
        ValueBase.__init__(self,
                           element_buffer_class=BoundingBoxBuffer,
                           x1=x1,
                           y1=y1,
                           x2=x2,
                           y2=y2,
                           score=score,
                           class_num=class_num)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def x1(self) -> float:
        """
        float: X coordinate of bounding box top-left corner.
        """
        return self._element_buffer._x1

    @x1.setter
    def x1(self, value: float):
        self._element_buffer._x1 = value

    @property
    def y1(self) -> float:
        """
        float: Y coordinate of bounding box top-left corner.
        """
        return self._element_buffer._y1

    @y1.setter
    def y1(self, value: float):
        self._element_buffer._y1 = value

    @property
    def x2(self) -> float:
        """
        float: X coordinate of bounding box bottom-right corner.
        """
        return self._element_buffer._x2

    @x2.setter
    def x2(self, value: float):
        self._element_buffer._x2 = value

    @property
    def y2(self) -> float:
        """
        float: Y coordinate of bounding box bottom-right corner.
        """
        return self._element_buffer._y2

    @y2.setter
    def y2(self, value: float):
        self._element_buffer._y2 = value

    @property
    def score(self) -> float:
        """
        float: Probability score.
        """
        return self._element_buffer._score

    @score.setter
    def score(self, value: float):
        self._element_buffer._score = value

    @property
    def class_num(self) -> int:
        """
        int: Class # (of many) with highest probability.
        """
        return self._element_buffer._class_num

    @class_num.setter
    def class_num(self, value: int):
        self._element_buffer._class_num = value

    def get_member_variable_dict(self) -> dict:
        return {
            'x1': self.x1,
            'y1': self.y1,
            'x2': self.x2,
            'y2': self.y2,
            'score': self.score,
            'class_num': self.class_num
        }


class Point(ValueBase, ValueRepresentBase):
    """
    Point descriptor.

    Attributes
    ----------
    x : int, default=0
        X coordinate of point.
    y : int, default=0
        Y coordinate of point.
    """

    def __init__(self,
                 x: int = 0,
                 y: int = 0):
        """
        Point descriptor.

        Parameters
        ----------
        x : int, default=0
            X coordinate of point.
        y : int, default=0
            Y coordinate of point.
        """
        ValueBase.__init__(self,
                           element_buffer_class=PointBuffer,
                           x=x,
                           y=y)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def x(self) -> float:
        """
        int: X coordinate of point.
        """
        return self._element_buffer._x

    @x.setter
    def x(self, value: float):
        self._element_buffer._x = value

    @property
    def y(self) -> float:
        """
        int: Y coordinate of point.
        """
        return self._element_buffer._y

    @y.setter
    def y(self, value: float):
        self._element_buffer._y = value

    def get_member_variable_dict(self) -> dict:
        return {
            'x': self.x,
            'y': self.y,
        }


class LandMark(ValueBase, ValueRepresentBase):
    """
    Landmark descriptor.

    Attributes
    ----------
    point_list : List[kp.Point], default=[]
        Landmark points.
    score : float, default=0
        Score of landmark.
    blur : float, default=0
        Blur score of landmark.
    class_num : int, default=0
        Class of landmark.
    """

    def __init__(self,
                 point_list: List[Point] = [],
                 score: float = 0,
                 blur: float = 0,
                 class_num: int = 0):
        """
        Landmark descriptor.

        Parameters
        ----------
        point_list : List[kp.Point], default=[]
            Landmark points.
        score : float, default=0
            Score of landmark.
        blur : float, default=0
            Blur score of landmark.
        class_num : int, default=0
            Class of landmark.
        """
        ValueBase.__init__(self,
                           element_buffer_class=LandMarkBuffer,
                           point_list=[PointBuffer(x=point.x,
                                                   y=point.y) for point in point_list],
                           score=score,
                           blur=blur,
                           class_num=class_num)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def point_list(self) -> List[Point]:
        """
        List[kp.Point]: Landmark points.
        """
        return [Point(x=point._x,
                      y=point._y) for point in self._element_buffer._marks]

    @point_list.setter
    def point_list(self, value: List[Point] = []):
        assert len(value) == Const.LAND_MARK_POINTS.value

        self._element_buffer._marks = (PointBuffer * Const.LAND_MARK_POINTS.value)(*[
            PointBuffer(x=point.x,
                        y=point.y) for point in value])

    @property
    def score(self) -> float:
        """
        int: Score of landmark.
        """
        return self._element_buffer._score

    @score.setter
    def score(self, value: float):
        self._element_buffer._score = value

    @property
    def blur(self) -> float:
        """
        int: Blur score of landmark.
        """
        return self._element_buffer._blur

    @blur.setter
    def blur(self, value: float):
        self._element_buffer._blur = value

    @property
    def class_num(self) -> int:
        """
        int: Class of landmark.
        """
        return self._element_buffer._class_num

    @class_num.setter
    def class_num(self, value: float):
        self._element_buffer._class_num = value

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'score': self.score,
            'blur': self.blur,
            'class_num': self.class_num,
            'point_list': {}
        }

        for idx, point_element in enumerate(self.point_list):
            member_variable_dict['point_list'][idx] = point_element.get_member_variable_dict()

        return member_variable_dict


class Classification(ValueBase, ValueRepresentBase):
    """
    Classification result descriptor.

    Attributes
    ----------
    class_num : int, default=0
        Class # (of many) with highest probability.
    score : float, default=0
        Probability of the class.
    """

    def __init__(self,
                 class_num: int = 0,
                 score: float = 0):
        """
        Classification result descriptor.

        Attributes
        ----------
        class_num : int, default=0
            Class # (of many) with highest probability.
        score : float, default=0
            Probability of the class.
        """
        ValueBase.__init__(self,
                           element_buffer_class=ClassificationBuffer,
                           class_num=class_num,
                           score=score)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def class_num(self) -> int:
        """
        int: Class # (of many) with highest probability.
        """
        return self._element_buffer._class_num

    @class_num.setter
    def class_num(self, value: int = 0):
        self._element_buffer._class_num = value

    @property
    def score(self) -> float:
        """
        int: Probability of the class.
        """
        return self._element_buffer._score

    @score.setter
    def score(self, value: float):
        self._element_buffer._score = value

    def get_member_variable_dict(self) -> dict:
        member_variable_dict = {
            'class_num': self.class_num,
            'score': self.score
        }

        return member_variable_dict


class FaceRecognize(ValueBase, ValueRepresentBase):
    """
    Describe feature map of one face.

    Attributes
    ----------
    feature_map : numpy.ndarray, default=np.zeros((Const.FR_FEAT_LENGTH.value,), dtype=numpy.float32)
        Floating-point feature map of one face.
    feature_map_fixed : numpy.ndarray, default=np.zeros((Const.FR_FEAT_LENGTH.value,), dtype=numpy.int8)
        Fixed-point feature map of one face.
    """

    def __init__(self,
                 feature_map: np.ndarray = np.zeros((Const.FR_FEAT_LENGTH.value,), dtype=np.float32),
                 feature_map_fixed: np.ndarray = np.zeros((Const.FR_FEAT_LENGTH.value,), dtype=np.int8)):
        """
        Describe feature map of one face.

        Parameters
        ----------
        feature_map : numpy.ndarray, default=np.zeros((Const.FR_FEAT_LENGTH.value,), dtype=numpy.float32)
            Floating-point feature map of one face.
        feature_map_fixed : numpy.ndarray, default=np.zeros((Const.FR_FEAT_LENGTH.value,), dtype=numpy.int8)
            Fixed-point feature map of one face.
        """
        ValueBase.__init__(self,
                           element_buffer_class=FaceRecognizeBuffer,
                           feature_map=feature_map,
                           feature_map_fixed=feature_map_fixed)

    def _cast_element_buffer(self) -> None:
        pass

    @property
    def feature_map(self) -> np.ndarray:
        """
        numpy.ndarray: Floating-point feature map of one face.
        """
        return np.ctypeslib.as_array(self._element_buffer._feature_map)

    @property
    def feature_map_fixed(self) -> np.ndarray:
        """
        numpy.ndarray: Fixed-point feature map of one face.
        """
        return np.ctypeslib.as_array(self._element_buffer._feature_map_fixed)

    def get_member_variable_dict(self) -> dict:
        return {
            'feature_map': self.feature_map,
            'feature_map_fixed': self.feature_map_fixed
        }
